text: // Copyright 2014 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "platform/graphics/gpu/WebGLImageConversion.h"
  
  #include "platform/graphics/ImageObserver.h"
  #include "platform/graphics/cpu/arm/WebGLImageConversionNEON.h"
  #include "platform/graphics/cpu/mips/WebGLImageConversionMSA.h"
  #include "platform/graphics/cpu/x86/WebGLImageConversionSSE.h"
  #include "platform/graphics/skia/SkiaUtils.h"
  #include "platform/image-decoders/ImageDecoder.h"
  #include "third_party/skia/include/core/SkImage.h"
  #include "wtf/CheckedNumeric.h"
  #include "wtf/PtrUtil.h"
  #include <memory>
  
  namespace blink {
  
  namespace {
  
  const float maxInt8Value = INT8_MAX;
  const float maxUInt8Value = UINT8_MAX;
  const float maxInt16Value = INT16_MAX;
  const float maxUInt16Value = UINT16_MAX;
  const double maxInt32Value = INT32_MAX;
  const double maxUInt32Value = UINT32_MAX;
  
  int8_t ClampMin(int8_t value) {
    const static int8_t minInt8Value = INT8_MIN + 1;
    return value < minInt8Value ? minInt8Value : value;
  }
  
  int16_t ClampMin(int16_t value) {
    const static int16_t minInt16Value = INT16_MIN + 1;
    return value < minInt16Value ? minInt16Value : value;
  }
  
  int32_t ClampMin(int32_t value) {
    const static int32_t minInt32Value = INT32_MIN + 1;
    return value < minInt32Value ? minInt32Value : value;
  }
  
  WebGLImageConversion::DataFormat getDataFormat(GLenum destinationFormat,
                                                 GLenum destinationType) {
    WebGLImageConversion::DataFormat dstFormat =
        WebGLImageConversion::DataFormatRGBA8;
    switch (destinationType) {
      case GL_BYTE:
        switch (destinationFormat) {
          case GL_RED:
          case GL_RED_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatR8_S;
            break;
          case GL_RG:
          case GL_RG_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRG8_S;
            break;
          case GL_RGB:
          case GL_RGB_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGB8_S;
            break;
          case GL_RGBA:
          case GL_RGBA_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGBA8_S;
            break;
          default:
            ASSERT_NOT_REACHED();
        }
        break;
      case GL_UNSIGNED_BYTE:
        switch (destinationFormat) {
          case GL_RGB:
          case GL_RGB_INTEGER:
          case GL_SRGB_EXT:
            dstFormat = WebGLImageConversion::DataFormatRGB8;
            break;
          case GL_RGBA:
          case GL_RGBA_INTEGER:
          case GL_SRGB_ALPHA_EXT:
            dstFormat = WebGLImageConversion::DataFormatRGBA8;
            break;
          case GL_ALPHA:
            dstFormat = WebGLImageConversion::DataFormatA8;
            break;
          case GL_LUMINANCE:
          case GL_RED:
          case GL_RED_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatR8;
            break;
          case GL_RG:
          case GL_RG_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRG8;
            break;
          case GL_LUMINANCE_ALPHA:
            dstFormat = WebGLImageConversion::DataFormatRA8;
            break;
          default:
            ASSERT_NOT_REACHED();
        }
        break;
      case GL_SHORT:
        switch (destinationFormat) {
          case GL_RED_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatR16_S;
            break;
          case GL_RG_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRG16_S;
            break;
          case GL_RGB_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGB16_S;
          case GL_RGBA_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGBA16_S;
          default:
            ASSERT_NOT_REACHED();
        }
        break;
      case GL_UNSIGNED_SHORT:
        switch (destinationFormat) {
          case GL_RED_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatR16;
            break;
          case GL_DEPTH_COMPONENT:
            dstFormat = WebGLImageConversion::DataFormatD16;
            break;
          case GL_RG_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRG16;
            break;
          case GL_RGB_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGB16;
            break;
          case GL_RGBA_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGBA16;
            break;
          default:
            ASSERT_NOT_REACHED();
        }
        break;
      case GL_INT:
        switch (destinationFormat) {
          case GL_RED_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatR32_S;
            break;
          case GL_RG_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRG32_S;
            break;
          case GL_RGB_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGB32_S;
            break;
          case GL_RGBA_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGBA32_S;
            break;
          default:
            ASSERT_NOT_REACHED();
        }
        break;
      case GL_UNSIGNED_INT:
        switch (destinationFormat) {
          case GL_RED_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatR32;
            break;
          case GL_DEPTH_COMPONENT:
            dstFormat = WebGLImageConversion::DataFormatD32;
            break;
          case GL_RG_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRG32;
            break;
          case GL_RGB_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGB32;
            break;
          case GL_RGBA_INTEGER:
            dstFormat = WebGLImageConversion::DataFormatRGBA32;
            break;
          default:
            ASSERT_NOT_REACHED();
        }
        break;
      case GL_HALF_FLOAT_OES:  // OES_texture_half_float
      case GL_HALF_FLOAT:
        switch (destinationFormat) {
          case GL_RGBA:
            dstFormat = WebGLImageConversion::DataFormatRGBA16F;
            break;
          case GL_RGB:
            dstFormat = WebGLImageConversion::DataFormatRGB16F;
            break;
          case GL_RG:
            dstFormat = WebGLImageConversion::DataFormatRG16F;
            break;
          case GL_ALPHA:
            dstFormat = WebGLImageConversion::DataFormatA16F;
            break;
          case GL_LUMINANCE:
          case GL_RED:
            dstFormat = WebGLImageConversion::DataFormatR16F;
            break;
          case GL_LUMINANCE_ALPHA:
            dstFormat = WebGLImageConversion::DataFormatRA16F;
            break;
          default:
            ASSERT_NOT_REACHED();
        }
        break;
      case GL_FLOAT:  // OES_texture_float
        switch (destinationFormat) {
          case GL_RGBA:
            dstFormat = WebGLImageConversion::DataFormatRGBA32F;
            break;
          case GL_RGB:
            dstFormat = WebGLImageConversion::DataFormatRGB32F;
            break;
          case GL_RG:
            dstFormat = WebGLImageConversion::DataFormatRG32F;
            break;
          case GL_ALPHA:
            dstFormat = WebGLImageConversion::DataFormatA32F;
            break;
          case GL_LUMINANCE:
          case GL_RED:
            dstFormat = WebGLImageConversion::DataFormatR32F;
            break;
          case GL_DEPTH_COMPONENT:
            dstFormat = WebGLImageConversion::DataFormatD32F;
            break;
          case GL_LUMINANCE_ALPHA:
            dstFormat = WebGLImageConversion::DataFormatRA32F;
            break;
          default:
            ASSERT_NOT_REACHED();
        }
        break;
      case GL_UNSIGNED_SHORT_4_4_4_4:
        dstFormat = WebGLImageConversion::DataFormatRGBA4444;
        break;
      case GL_UNSIGNED_SHORT_5_5_5_1:
        dstFormat = WebGLImageConversion::DataFormatRGBA5551;
        break;
      case GL_UNSIGNED_SHORT_5_6_5:
        dstFormat = WebGLImageConversion::DataFormatRGB565;
        break;
      case GL_UNSIGNED_INT_5_9_9_9_REV:
        dstFormat = WebGLImageConversion::DataFormatRGB5999;
        break;
      case GL_UNSIGNED_INT_24_8:
        dstFormat = WebGLImageConversion::DataFormatDS24_8;
        break;
      case GL_UNSIGNED_INT_10F_11F_11F_REV:
        dstFormat = WebGLImageConversion::DataFormatRGB10F11F11F;
        break;
      case GL_UNSIGNED_INT_2_10_10_10_REV:
        dstFormat = WebGLImageConversion::DataFormatRGBA2_10_10_10;
        break;
      default:
        ASSERT_NOT_REACHED();
    }
    return dstFormat;
  }
  
  // The following Float to Half-Float conversion code is from the implementation
  // of ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf, "Fast Half
  // Float Conversions" by Jeroen van der Zijp, November 2008 (Revised September
  // 2010). Specially, the basetable[512] and shifttable[512] are generated as
  // follows:
  /*
  unsigned short basetable[512];
  unsigned char shifttable[512];
  
  void generatetables(){
      unsigned int i;
      int e;
      for (i = 0; i < 256; ++i){
          e = i - 127;
          if (e < -24){ // Very small numbers map to zero
              basetable[i | 0x000] = 0x0000;
              basetable[i | 0x100] = 0x8000;
              shifttable[i | 0x000] = 24;
              shifttable[i | 0x100] = 24;
          }
          else if (e < -14) { // Small numbers map to denorms
              basetable[i | 0x000] = (0x0400>>(-e-14));
              basetable[i | 0x100] = (0x0400>>(-e-14)) | 0x8000;
              shifttable[i | 0x000] = -e-1;
              shifttable[i | 0x100] = -e-1;
          }
          else if (e <= 15){ // Normal numbers just lose precision
              basetable[i | 0x000] = ((e+15)<<10);
              basetable[i| 0x100] = ((e+15)<<10) | 0x8000;
              shifttable[i|0x000] = 13;
              shifttable[i|0x100] = 13;
          }
          else if (e<128){ // Large numbers map to Infinity
              basetable[i|0x000] = 0x7C00;
              basetable[i|0x100] = 0xFC00;
              shifttable[i|0x000] = 24;
              shifttable[i|0x100] = 24;
          }
          else { // Infinity and NaN's stay Infinity and NaN's
              basetable[i|0x000] = 0x7C00;
              basetable[i|0x100] = 0xFC00;
              shifttable[i|0x000] = 13;
              shifttable[i|0x100] = 13;
         }
      }
  }
  */
  
  unsigned short baseTable[512] = {
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
      0,     0,     0,     0,     1,     2,     4,     8,     16,    32,    64,
      128,   256,   512,   1024,  2048,  3072,  4096,  5120,  6144,  7168,  8192,
      9216,  10240, 11264, 12288, 13312, 14336, 15360, 16384, 17408, 18432, 19456,
      20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
      31744, 31744, 31744, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
      32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
      32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
      32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
      32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
      32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
      32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
      32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
      32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
      32768, 32768, 32768, 32768, 32768, 32768, 32768, 32769, 32770, 32772, 32776,
      32784, 32800, 32832, 32896, 33024, 33280, 33792, 34816, 35840, 36864, 37888,
      38912, 39936, 40960, 41984, 43008, 44032, 45056, 46080, 47104, 48128, 49152,
      50176, 51200, 52224, 53248, 54272, 55296, 56320, 57344, 58368, 59392, 60416,
      61440, 62464, 63488, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
      64512, 64512, 64512, 64512, 64512, 64512};
  
  unsigned char shiftTable[512] = {
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,
      13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
      13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 23, 22,
      21, 20, 19, 18, 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
      13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13};
  
  unsigned short convertFloatToHalfFloat(float f) {
    unsigned temp = *(reinterpret_cast<unsigned*>(&f));
    unsigned signexp = (temp >> 23) & 0x1ff;
    return baseTable[signexp] + ((temp & 0x007fffff) >> shiftTable[signexp]);
  }
  
  /* BEGIN CODE SHARED WITH MOZILLA FIREFOX */
  
  // The following packing and unpacking routines are expressed in terms of
  // function templates and inline functions to achieve generality and speedup.
  // Explicit template specializations correspond to the cases that would occur.
  // Some code are merged back from Mozilla code in
  // http://mxr.mozilla.org/mozilla-central/source/content/canvas/src/WebGLTexelConversions.h
  
  //----------------------------------------------------------------------
  // Pixel unpacking routines.
  template <int format, typename SourceType, typename DstType>
  void unpack(const SourceType*, DstType*, unsigned) {
    ASSERT_NOT_REACHED();
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatARGB8, uint8_t, uint8_t>(
      const uint8_t* source,
      uint8_t* destination,
      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[1];
      destination[1] = source[2];
      destination[2] = source[3];
      destination[3] = source[0];
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatABGR8, uint8_t, uint8_t>(
      const uint8_t* source,
      uint8_t* destination,
      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[3];
      destination[1] = source[2];
      destination[2] = source[1];
      destination[3] = source[0];
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatBGRA8, uint8_t, uint8_t>(
      const uint8_t* source,
      uint8_t* destination,
      unsigned pixelsPerRow) {
    const uint32_t* source32 = reinterpret_cast_ptr<const uint32_t*>(source);
    uint32_t* destination32 = reinterpret_cast_ptr<uint32_t*>(destination);
  
  #if CPU(X86) || CPU(X86_64)
    SIMD::unpackOneRowOfBGRA8LittleToRGBA8(source32, destination32, pixelsPerRow);
  #endif
  #if HAVE(MIPS_MSA_INTRINSICS)
    SIMD::unpackOneRowOfBGRA8LittleToRGBA8MSA(source32, destination32,
                                              pixelsPerRow);
  #endif
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      uint32_t bgra = source32[i];
  #if CPU(BIG_ENDIAN)
      uint32_t brMask = 0xff00ff00;
      uint32_t gaMask = 0x00ff00ff;
  #else
      uint32_t brMask = 0x00ff00ff;
      uint32_t gaMask = 0xff00ff00;
  #endif
      uint32_t rgba = (((bgra >> 16) | (bgra << 16)) & brMask) | (bgra & gaMask);
      destination32[i] = rgba;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatRGBA5551, uint16_t, uint8_t>(
      const uint16_t* source,
      uint8_t* destination,
      unsigned pixelsPerRow) {
  #if CPU(X86) || CPU(X86_64)
    SIMD::unpackOneRowOfRGBA5551LittleToRGBA8(source, destination, pixelsPerRow);
  #endif
  #if HAVE(ARM_NEON_INTRINSICS)
    SIMD::unpackOneRowOfRGBA5551ToRGBA8(source, destination, pixelsPerRow);
  #endif
  #if HAVE(MIPS_MSA_INTRINSICS)
    SIMD::unpackOneRowOfRGBA5551ToRGBA8MSA(source, destination, pixelsPerRow);
  #endif
  
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      uint16_t packedValue = source[0];
      uint8_t r = packedValue >> 11;
      uint8_t g = (packedValue >> 6) & 0x1F;
      uint8_t b = (packedValue >> 1) & 0x1F;
      destination[0] = (r << 3) | (r & 0x7);
      destination[1] = (g << 3) | (g & 0x7);
      destination[2] = (b << 3) | (b & 0x7);
      destination[3] = (packedValue & 0x1) ? 0xFF : 0x0;
      source += 1;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatRGBA4444, uint16_t, uint8_t>(
      const uint16_t* source,
      uint8_t* destination,
      unsigned pixelsPerRow) {
  #if CPU(X86) || CPU(X86_64)
    SIMD::unpackOneRowOfRGBA4444LittleToRGBA8(source, destination, pixelsPerRow);
  #endif
  #if HAVE(ARM_NEON_INTRINSICS)
    SIMD::unpackOneRowOfRGBA4444ToRGBA8(source, destination, pixelsPerRow);
  #endif
  #if HAVE(MIPS_MSA_INTRINSICS)
    SIMD::unpackOneRowOfRGBA4444ToRGBA8MSA(source, destination, pixelsPerRow);
  #endif
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      uint16_t packedValue = source[0];
      uint8_t r = packedValue >> 12;
      uint8_t g = (packedValue >> 8) & 0x0F;
      uint8_t b = (packedValue >> 4) & 0x0F;
      uint8_t a = packedValue & 0x0F;
      destination[0] = r << 4 | r;
      destination[1] = g << 4 | g;
      destination[2] = b << 4 | b;
      destination[3] = a << 4 | a;
      source += 1;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatRA8, uint8_t, uint8_t>(
      const uint8_t* source,
      uint8_t* destination,
      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      destination[1] = source[0];
      destination[2] = source[0];
      destination[3] = source[1];
      source += 2;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatAR8, uint8_t, uint8_t>(
      const uint8_t* source,
      uint8_t* destination,
      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[1];
      destination[1] = source[1];
      destination[2] = source[1];
      destination[3] = source[0];
      source += 2;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatRGBA8, uint8_t, float>(
      const uint8_t* source,
      float* destination,
      unsigned pixelsPerRow) {
    const float scaleFactor = 1.0f / 255.0f;
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0] * scaleFactor;
      destination[1] = source[1] * scaleFactor;
      destination[2] = source[2] * scaleFactor;
      destination[3] = source[3] * scaleFactor;
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatBGRA8, uint8_t, float>(
      const uint8_t* source,
      float* destination,
      unsigned pixelsPerRow) {
    const float scaleFactor = 1.0f / 255.0f;
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[2] * scaleFactor;
      destination[1] = source[1] * scaleFactor;
      destination[2] = source[0] * scaleFactor;
      destination[3] = source[3] * scaleFactor;
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatABGR8, uint8_t, float>(
      const uint8_t* source,
      float* destination,
      unsigned pixelsPerRow) {
    const float scaleFactor = 1.0f / 255.0f;
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[3] * scaleFactor;
      destination[1] = source[2] * scaleFactor;
      destination[2] = source[1] * scaleFactor;
      destination[3] = source[0] * scaleFactor;
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatARGB8, uint8_t, float>(
      const uint8_t* source,
      float* destination,
      unsigned pixelsPerRow) {
    const float scaleFactor = 1.0f / 255.0f;
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[1] * scaleFactor;
      destination[1] = source[2] * scaleFactor;
      destination[2] = source[3] * scaleFactor;
      destination[3] = source[0] * scaleFactor;
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatRA32F, float, float>(
      const float* source,
      float* destination,
      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      destination[1] = source[0];
      destination[2] = source[0];
      destination[3] = source[1];
      source += 2;
      destination += 4;
    }
  }
  
  template <>
  void unpack<WebGLImageConversion::DataFormatRGBA2_10_10_10, uint32_t, float>(
      const uint32_t* source,
      float* destination,
      unsigned pixelsPerRow) {
    static const float rgbScaleFactor = 1.0f / 1023.0f;
    static const float alphaScaleFactor = 1.0f / 3.0f;
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      uint32_t packedValue = source[0];
      destination[0] = static_cast<float>(packedValue & 0x3FF) * rgbScaleFactor;
      destination[1] =
          static_cast<float>((packedValue >> 10) & 0x3FF) * rgbScaleFactor;
      destination[2] =
          static_cast<float>((packedValue >> 20) & 0x3FF) * rgbScaleFactor;
      destination[3] = static_cast<float>(packedValue >> 30) * alphaScaleFactor;
      source += 1;
      destination += 4;
    }
  }
  
  //----------------------------------------------------------------------
  // Pixel packing routines.
  //
  
  template <int format, int alphaOp, typename SourceType, typename DstType>
  void pack(const SourceType*, DstType*, unsigned) {
    ASSERT_NOT_REACHED();
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatA8,
            WebGLImageConversion::AlphaDoNothing,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[3];
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatR8,
            WebGLImageConversion::AlphaDoNothing,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatR8,
            WebGLImageConversion::AlphaDoPremultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] / 255.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      destination[0] = sourceR;
      source += 4;
      destination += 1;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatR8,
            WebGLImageConversion::AlphaDoUnmultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
  #if CPU(X86) || CPU(X86_64)
    SIMD::packOneRowOfRGBA8LittleToR8(source, destination, pixelsPerRow);
  #endif
  #if HAVE(MIPS_MSA_INTRINSICS)
    SIMD::packOneRowOfRGBA8LittleToR8MSA(source, destination, pixelsPerRow);
  #endif
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 255.0f / source[3] : 1.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      destination[0] = sourceR;
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRA8,
            WebGLImageConversion::AlphaDoNothing,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      destination[1] = source[3];
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRA8,
            WebGLImageConversion::AlphaDoPremultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] / 255.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      destination[0] = sourceR;
      destination[1] = source[3];
      source += 4;
      destination += 2;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatRA8,
            WebGLImageConversion::AlphaDoUnmultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
  #if CPU(X86) || CPU(X86_64)
    SIMD::packOneRowOfRGBA8LittleToRA8(source, destination, pixelsPerRow);
  #endif
  #if HAVE(MIPS_MSA_INTRINSICS)
    SIMD::packOneRowOfRGBA8LittleToRA8MSA(source, destination, pixelsPerRow);
  #endif
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 255.0f / source[3] : 1.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      destination[0] = sourceR;
      destination[1] = source[3];
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB8,
            WebGLImageConversion::AlphaDoNothing,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      destination[1] = source[1];
      destination[2] = source[2];
      source += 4;
      destination += 3;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB8,
            WebGLImageConversion::AlphaDoPremultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] / 255.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      destination[0] = sourceR;
      destination[1] = sourceG;
      destination[2] = sourceB;
      source += 4;
      destination += 3;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatRGB8,
            WebGLImageConversion::AlphaDoUnmultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 255.0f / source[3] : 1.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      destination[0] = sourceR;
      destination[1] = sourceG;
      destination[2] = sourceB;
      source += 4;
      destination += 3;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA8,
            WebGLImageConversion::AlphaDoPremultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] / 255.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      destination[0] = sourceR;
      destination[1] = sourceG;
      destination[2] = sourceB;
      destination[3] = source[3];
      source += 4;
      destination += 4;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA8,
            WebGLImageConversion::AlphaDoUnmultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
  #if CPU(X86) || CPU(X86_64)
    SIMD::packOneRowOfRGBA8LittleToRGBA8(source, destination, pixelsPerRow);
  #endif
  #if HAVE(MIPS_MSA_INTRINSICS)
    SIMD::packOneRowOfRGBA8LittleToRGBA8MSA(source, destination, pixelsPerRow);
  #endif
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 255.0f / source[3] : 1.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      destination[0] = sourceR;
      destination[1] = sourceG;
      destination[2] = sourceB;
      destination[3] = source[3];
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA4444,
            WebGLImageConversion::AlphaDoNothing,
            uint8_t,
            uint16_t>(const uint8_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
  #if HAVE(ARM_NEON_INTRINSICS)
    SIMD::packOneRowOfRGBA8ToUnsignedShort4444(source, destination, pixelsPerRow);
  #endif
  #if HAVE(MIPS_MSA_INTRINSICS)
    SIMD::packOneRowOfRGBA8ToUnsignedShort4444MSA(source, destination,
                                                  pixelsPerRow);
  #endif
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      *destination = (((source[0] & 0xF0) << 8) | ((source[1] & 0xF0) << 4) |
                      (source[2] & 0xF0) | (source[3] >> 4));
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA4444,
            WebGLImageConversion::AlphaDoPremultiply,
            uint8_t,
            uint16_t>(const uint8_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] / 255.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      *destination = (((sourceR & 0xF0) << 8) | ((sourceG & 0xF0) << 4) |
                      (sourceB & 0xF0) | (source[3] >> 4));
      source += 4;
      destination += 1;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA4444,
            WebGLImageConversion::AlphaDoUnmultiply,
            uint8_t,
            uint16_t>(const uint8_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 255.0f / source[3] : 1.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      *destination = (((sourceR & 0xF0) << 8) | ((sourceG & 0xF0) << 4) |
                      (sourceB & 0xF0) | (source[3] >> 4));
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA5551,
            WebGLImageConversion::AlphaDoNothing,
            uint8_t,
            uint16_t>(const uint8_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
  #if HAVE(ARM_NEON_INTRINSICS)
    SIMD::packOneRowOfRGBA8ToUnsignedShort5551(source, destination, pixelsPerRow);
  #endif
  #if HAVE(MIPS_MSA_INTRINSICS)
    SIMD::packOneRowOfRGBA8ToUnsignedShort5551MSA(source, destination,
                                                  pixelsPerRow);
  #endif
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      *destination = (((source[0] & 0xF8) << 8) | ((source[1] & 0xF8) << 3) |
                      ((source[2] & 0xF8) >> 2) | (source[3] >> 7));
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA5551,
            WebGLImageConversion::AlphaDoPremultiply,
            uint8_t,
            uint16_t>(const uint8_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] / 255.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      *destination = (((sourceR & 0xF8) << 8) | ((sourceG & 0xF8) << 3) |
                      ((sourceB & 0xF8) >> 2) | (source[3] >> 7));
      source += 4;
      destination += 1;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA5551,
            WebGLImageConversion::AlphaDoUnmultiply,
            uint8_t,
            uint16_t>(const uint8_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 255.0f / source[3] : 1.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      *destination = (((sourceR & 0xF8) << 8) | ((sourceG & 0xF8) << 3) |
                      ((sourceB & 0xF8) >> 2) | (source[3] >> 7));
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB565,
            WebGLImageConversion::AlphaDoNothing,
            uint8_t,
            uint16_t>(const uint8_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
  #if HAVE(ARM_NEON_INTRINSICS)
    SIMD::packOneRowOfRGBA8ToUnsignedShort565(source, destination, pixelsPerRow);
  #endif
  #if HAVE(MIPS_MSA_INTRINSICS)
    SIMD::packOneRowOfRGBA8ToUnsignedShort565MSA(source, destination,
                                                 pixelsPerRow);
  #endif
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      *destination = (((source[0] & 0xF8) << 8) | ((source[1] & 0xFC) << 3) |
                      ((source[2] & 0xF8) >> 3));
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB565,
            WebGLImageConversion::AlphaDoPremultiply,
            uint8_t,
            uint16_t>(const uint8_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] / 255.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      *destination = (((sourceR & 0xF8) << 8) | ((sourceG & 0xFC) << 3) |
                      ((sourceB & 0xF8) >> 3));
      source += 4;
      destination += 1;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatRGB565,
            WebGLImageConversion::AlphaDoUnmultiply,
            uint8_t,
            uint16_t>(const uint8_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 255.0f / source[3] : 1.0f;
      uint8_t sourceR =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      uint8_t sourceG =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      uint8_t sourceB =
          static_cast<uint8_t>(static_cast<float>(source[2]) * scaleFactor);
      *destination = (((sourceR & 0xF8) << 8) | ((sourceG & 0xFC) << 3) |
                      ((sourceB & 0xF8) >> 3));
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB32F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      destination[1] = source[1];
      destination[2] = source[2];
      source += 4;
      destination += 3;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB32F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = source[0] * scaleFactor;
      destination[1] = source[1] * scaleFactor;
      destination[2] = source[2] * scaleFactor;
      source += 4;
      destination += 3;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB32F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = source[0] * scaleFactor;
      destination[1] = source[1] * scaleFactor;
      destination[2] = source[2] * scaleFactor;
      source += 4;
      destination += 3;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA32F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = source[0] * scaleFactor;
      destination[1] = source[1] * scaleFactor;
      destination[2] = source[2] * scaleFactor;
      destination[3] = source[3];
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA32F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = source[0] * scaleFactor;
      destination[1] = source[1] * scaleFactor;
      destination[2] = source[2] * scaleFactor;
      destination[3] = source[3];
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatA32F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[3];
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatR32F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatR32F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = source[0] * scaleFactor;
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatR32F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = source[0] * scaleFactor;
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRA32F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      destination[1] = source[3];
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRA32F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = source[0] * scaleFactor;
      destination[1] = source[3];
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRA32F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = source[0] * scaleFactor;
      destination[1] = source[3];
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA16F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = convertFloatToHalfFloat(source[0]);
      destination[1] = convertFloatToHalfFloat(source[1]);
      destination[2] = convertFloatToHalfFloat(source[2]);
      destination[3] = convertFloatToHalfFloat(source[3]);
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA16F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      destination[1] = convertFloatToHalfFloat(source[1] * scaleFactor);
      destination[2] = convertFloatToHalfFloat(source[2] * scaleFactor);
      destination[3] = convertFloatToHalfFloat(source[3]);
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA16F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      destination[1] = convertFloatToHalfFloat(source[1] * scaleFactor);
      destination[2] = convertFloatToHalfFloat(source[2] * scaleFactor);
      destination[3] = convertFloatToHalfFloat(source[3]);
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB16F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = convertFloatToHalfFloat(source[0]);
      destination[1] = convertFloatToHalfFloat(source[1]);
      destination[2] = convertFloatToHalfFloat(source[2]);
      source += 4;
      destination += 3;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB16F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      destination[1] = convertFloatToHalfFloat(source[1] * scaleFactor);
      destination[2] = convertFloatToHalfFloat(source[2] * scaleFactor);
      source += 4;
      destination += 3;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGB16F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      destination[1] = convertFloatToHalfFloat(source[1] * scaleFactor);
      destination[2] = convertFloatToHalfFloat(source[2] * scaleFactor);
      source += 4;
      destination += 3;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRA16F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = convertFloatToHalfFloat(source[0]);
      destination[1] = convertFloatToHalfFloat(source[3]);
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRA16F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      destination[1] = convertFloatToHalfFloat(source[3]);
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRA16F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      destination[1] = convertFloatToHalfFloat(source[3]);
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatR16F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = convertFloatToHalfFloat(source[0]);
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatR16F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatR16F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatA16F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = convertFloatToHalfFloat(source[3]);
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA8_S,
            WebGLImageConversion::AlphaDoPremultiply,
            int8_t,
            int8_t>(const int8_t* source,
                    int8_t* destination,
                    unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[3] = ClampMin(source[3]);
      float scaleFactor = static_cast<float>(destination[3]) / maxInt8Value;
      destination[0] = static_cast<int8_t>(
          static_cast<float>(ClampMin(source[0])) * scaleFactor);
      destination[1] = static_cast<int8_t>(
          static_cast<float>(ClampMin(source[1])) * scaleFactor);
      destination[2] = static_cast<int8_t>(
          static_cast<float>(ClampMin(source[2])) * scaleFactor);
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA16,
            WebGLImageConversion::AlphaDoPremultiply,
            uint16_t,
            uint16_t>(const uint16_t* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = static_cast<float>(source[3]) / maxUInt16Value;
      destination[0] =
          static_cast<uint16_t>(static_cast<float>(source[0]) * scaleFactor);
      destination[1] =
          static_cast<uint16_t>(static_cast<float>(source[1]) * scaleFactor);
      destination[2] =
          static_cast<uint16_t>(static_cast<float>(source[2]) * scaleFactor);
      destination[3] = source[3];
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA16_S,
            WebGLImageConversion::AlphaDoPremultiply,
            int16_t,
            int16_t>(const int16_t* source,
                     int16_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[3] = ClampMin(source[3]);
      float scaleFactor = static_cast<float>(destination[3]) / maxInt16Value;
      destination[0] = static_cast<int16_t>(
          static_cast<float>(ClampMin(source[0])) * scaleFactor);
      destination[1] = static_cast<int16_t>(
          static_cast<float>(ClampMin(source[1])) * scaleFactor);
      destination[2] = static_cast<int16_t>(
          static_cast<float>(ClampMin(source[2])) * scaleFactor);
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA32,
            WebGLImageConversion::AlphaDoPremultiply,
            uint32_t,
            uint32_t>(const uint32_t* source,
                      uint32_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      double scaleFactor = static_cast<double>(source[3]) / maxUInt32Value;
      destination[0] =
          static_cast<uint32_t>(static_cast<double>(source[0]) * scaleFactor);
      destination[1] =
          static_cast<uint32_t>(static_cast<double>(source[1]) * scaleFactor);
      destination[2] =
          static_cast<uint32_t>(static_cast<double>(source[2]) * scaleFactor);
      destination[3] = source[3];
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA32_S,
            WebGLImageConversion::AlphaDoPremultiply,
            int32_t,
            int32_t>(const int32_t* source,
                     int32_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[3] = ClampMin(source[3]);
      double scaleFactor = static_cast<double>(destination[3]) / maxInt32Value;
      destination[0] = static_cast<int32_t>(
          static_cast<double>(ClampMin(source[0])) * scaleFactor);
      destination[1] = static_cast<int32_t>(
          static_cast<double>(ClampMin(source[1])) * scaleFactor);
      destination[2] = static_cast<int32_t>(
          static_cast<double>(ClampMin(source[2])) * scaleFactor);
      source += 4;
      destination += 4;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRGBA2_10_10_10,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            uint32_t>(const float* source,
                      uint32_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      uint32_t r = static_cast<uint32_t>(source[0] * source[3] * 1023.0f);
      uint32_t g = static_cast<uint32_t>(source[1] * source[3] * 1023.0f);
      uint32_t b = static_cast<uint32_t>(source[2] * source[3] * 1023.0f);
      uint32_t a = static_cast<uint32_t>(source[3] * 3.0f);
      destination[0] = (a << 30) | (b << 20) | (g << 10) | r;
      source += 4;
      destination += 1;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRG8,
            WebGLImageConversion::AlphaDoNothing,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      destination[1] = source[1];
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRG8,
            WebGLImageConversion::AlphaDoPremultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = static_cast<float>(source[3]) / maxUInt8Value;
      destination[0] =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      destination[1] =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      source += 4;
      destination += 2;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatRG8,
            WebGLImageConversion::AlphaDoUnmultiply,
            uint8_t,
            uint8_t>(const uint8_t* source,
                     uint8_t* destination,
                     unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor =
          source[3] ? maxUInt8Value / static_cast<float>(source[3]) : 1.0f;
      destination[0] =
          static_cast<uint8_t>(static_cast<float>(source[0]) * scaleFactor);
      destination[1] =
          static_cast<uint8_t>(static_cast<float>(source[1]) * scaleFactor);
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRG16F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = convertFloatToHalfFloat(source[0]);
      destination[1] = convertFloatToHalfFloat(source[1]);
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRG16F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      destination[1] = convertFloatToHalfFloat(source[1] * scaleFactor);
      source += 4;
      destination += 2;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatRG16F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            uint16_t>(const float* source,
                      uint16_t* destination,
                      unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = convertFloatToHalfFloat(source[0] * scaleFactor);
      destination[1] = convertFloatToHalfFloat(source[1] * scaleFactor);
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRG32F,
            WebGLImageConversion::AlphaDoNothing,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      destination[0] = source[0];
      destination[1] = source[1];
      source += 4;
      destination += 2;
    }
  }
  
  template <>
  void pack<WebGLImageConversion::DataFormatRG32F,
            WebGLImageConversion::AlphaDoPremultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3];
      destination[0] = source[0] * scaleFactor;
      destination[1] = source[1] * scaleFactor;
      source += 4;
      destination += 2;
    }
  }
  
  // FIXME: this routine is lossy and must be removed.
  template <>
  void pack<WebGLImageConversion::DataFormatRG32F,
            WebGLImageConversion::AlphaDoUnmultiply,
            float,
            float>(const float* source,
                   float* destination,
                   unsigned pixelsPerRow) {
    for (unsigned i = 0; i < pixelsPerRow; ++i) {
      float scaleFactor = source[3] ? 1.0f / source[3] : 1.0f;
      destination[0] = source[0] * scaleFactor;
      destination[1] = source[1] * scaleFactor;
      source += 4;
      destination += 2;
    }
  }
  
  bool HasAlpha(int format) {
    return format == WebGLImageConversion::DataFormatA8 ||
           format == WebGLImageConversion::DataFormatA16F ||
           format == WebGLImageConversion::DataFormatA32F ||
           format == WebGLImageConversion::DataFormatRA8 ||
           format == WebGLImageConversion::DataFormatAR8 ||
           format == WebGLImageConversion::DataFormatRA16F ||
           format == WebGLImageConversion::DataFormatRA32F ||
           format == WebGLImageConversion::DataFormatRGBA8 ||
           format == WebGLImageConversion::DataFormatBGRA8 ||
           format == WebGLImageConversion::DataFormatARGB8 ||
           format == WebGLImageConversion::DataFormatABGR8 ||
           format == WebGLImageConversion::DataFormatRGBA16F ||
           format == WebGLImageConversion::DataFormatRGBA32F ||
           format == WebGLImageConversion::DataFormatRGBA4444 ||
           format == WebGLImageConversion::DataFormatRGBA5551 ||
           format == WebGLImageConversion::DataFormatRGBA8_S ||
           format == WebGLImageConversion::DataFormatRGBA16 ||
           format == WebGLImageConversion::DataFormatRGBA16_S ||
           format == WebGLImageConversion::DataFormatRGBA32 ||
           format == WebGLImageConversion::DataFormatRGBA32_S ||
           format == WebGLImageConversion::DataFormatRGBA2_10_10_10;
  }
  
  bool HasColor(int format) {
    return format == WebGLImageConversion::DataFormatRGBA8 ||
           format == WebGLImageConversion::DataFormatRGBA16F ||
           format == WebGLImageConversion::DataFormatRGBA32F ||
           format == WebGLImageConversion::DataFormatRGB8 ||
           format == WebGLImageConversion::DataFormatRGB16F ||
           format == WebGLImageConversion::DataFormatRGB32F ||
           format == WebGLImageConversion::DataFormatBGR8 ||
           format == WebGLImageConversion::DataFormatBGRA8 ||
           format == WebGLImageConversion::DataFormatARGB8 ||
           format == WebGLImageConversion::DataFormatABGR8 ||
           format == WebGLImageConversion::DataFormatRGBA5551 ||
           format == WebGLImageConversion::DataFormatRGBA4444 ||
           format == WebGLImageConversion::DataFormatRGB565 ||
           format == WebGLImageConversion::DataFormatR8 ||
           format == WebGLImageConversion::DataFormatR16F ||
           format == WebGLImageConversion::DataFormatR32F ||
           format == WebGLImageConversion::DataFormatRA8 ||
           format == WebGLImageConversion::DataFormatRA16F ||
           format == WebGLImageConversion::DataFormatRA32F ||
           format == WebGLImageConversion::DataFormatAR8 ||
           format == WebGLImageConversion::DataFormatRGBA8_S ||
           format == WebGLImageConversion::DataFormatRGBA16 ||
           format == WebGLImageConversion::DataFormatRGBA16_S ||
           format == WebGLImageConversion::DataFormatRGBA32 ||
           format == WebGLImageConversion::DataFormatRGBA32_S ||
           format == WebGLImageConversion::DataFormatRGBA2_10_10_10 ||
           format == WebGLImageConversion::DataFormatRGB8_S ||
           format == WebGLImageConversion::DataFormatRGB16 ||
           format == WebGLImageConversion::DataFormatRGB16_S ||
           format == WebGLImageConversion::DataFormatRGB32 ||
           format == WebGLImageConversion::DataFormatRGB32_S ||
           format == WebGLImageConversion::DataFormatRGB10F11F11F ||
           format == WebGLImageConversion::DataFormatRGB5999 ||
           format == WebGLImageConversion::DataFormatRG8 ||
           format == WebGLImageConversion::DataFormatRG8_S ||
           format == WebGLImageConversion::DataFormatRG16 ||
           format == WebGLImageConversion::DataFormatRG16_S ||
           format == WebGLImageConversion::DataFormatRG32 ||
           format == WebGLImageConversion::DataFormatRG32_S ||
           format == WebGLImageConversion::DataFormatRG16F ||
           format == WebGLImageConversion::DataFormatRG32F ||
           format == WebGLImageConversion::DataFormatR8_S ||
           format == WebGLImageConversion::DataFormatR16 ||
           format == WebGLImageConversion::DataFormatR16_S ||
           format == WebGLImageConversion::DataFormatR32 ||
           format == WebGLImageConversion::DataFormatR32_S;
  }
  
  template <int Format>
  struct IsInt8Format {
    STATIC_ONLY(IsInt8Format);
    static const bool value = Format == WebGLImageConversion::DataFormatRGBA8_S ||
                              Format == WebGLImageConversion::DataFormatRGB8_S ||
                              Format == WebGLImageConversion::DataFormatRG8_S ||
                              Format == WebGLImageConversion::DataFormatR8_S;
  };
  
  template <int Format>
  struct IsInt16Format {
    STATIC_ONLY(IsInt16Format);
    static const bool value =
        Format == WebGLImageConversion::DataFormatRGBA16_S ||
        Format == WebGLImageConversion::DataFormatRGB16_S ||
        Format == WebGLImageConversion::DataFormatRG16_S ||
        Format == WebGLImageConversion::DataFormatR16_S;
  };
  
  template <int Format>
  struct IsInt32Format {
    STATIC_ONLY(IsInt32Format);
    static const bool value =
        Format == WebGLImageConversion::DataFormatRGBA32_S ||
        Format == WebGLImageConversion::DataFormatRGB32_S ||
        Format == WebGLImageConversion::DataFormatRG32_S ||
        Format == WebGLImageConversion::DataFormatR32_S;
  };
  
  template <int Format>
  struct IsUInt8Format {
    STATIC_ONLY(IsUInt8Format);
    static const bool value = Format == WebGLImageConversion::DataFormatRGBA8 ||
                              Format == WebGLImageConversion::DataFormatRGB8 ||
                              Format == WebGLImageConversion::DataFormatRG8 ||
                              Format == WebGLImageConversion::DataFormatR8 ||
                              Format == WebGLImageConversion::DataFormatBGRA8 ||
                              Format == WebGLImageConversion::DataFormatBGR8 ||
                              Format == WebGLImageConversion::DataFormatARGB8 ||
                              Format == WebGLImageConversion::DataFormatABGR8 ||
                              Format == WebGLImageConversion::DataFormatRA8 ||
                              Format == WebGLImageConversion::DataFormatAR8 ||
                              Format == WebGLImageConversion::DataFormatA8;
  };
  
  template <int Format>
  struct IsUInt16Format {
    STATIC_ONLY(IsUInt16Format);
    static const bool value = Format == WebGLImageConversion::DataFormatRGBA16 ||
                              Format == WebGLImageConversion::DataFormatRGB16 ||
                              Format == WebGLImageConversion::DataFormatRG16 ||
                              Format == WebGLImageConversion::DataFormatR16;
  };
  
  template <int Format>
  struct IsUInt32Format {
    STATIC_ONLY(IsUInt32Format);
    static const bool value = Format == WebGLImageConversion::DataFormatRGBA32 ||
                              Format == WebGLImageConversion::DataFormatRGB32 ||
                              Format == WebGLImageConversion::DataFormatRG32 ||
                              Format == WebGLImageConversion::DataFormatR32;
  };
  
  template <int Format>
  struct IsFloatFormat {
    STATIC_ONLY(IsFloatFormat);
    static const bool value = Format == WebGLImageConversion::DataFormatRGBA32F ||
                              Format == WebGLImageConversion::DataFormatRGB32F ||
                              Format == WebGLImageConversion::DataFormatRA32F ||
                              Format == WebGLImageConversion::DataFormatR32F ||
                              Format == WebGLImageConversion::DataFormatA32F ||
                              Format == WebGLImageConversion::DataFormatRG32F;
  };
  
  template <int Format>
  struct IsHalfFloatFormat {
    STATIC_ONLY(IsHalfFloatFormat);
    static const bool value = Format == WebGLImageConversion::DataFormatRGBA16F ||
                              Format == WebGLImageConversion::DataFormatRGB16F ||
                              Format == WebGLImageConversion::DataFormatRA16F ||
                              Format == WebGLImageConversion::DataFormatR16F ||
                              Format == WebGLImageConversion::DataFormatA16F ||
                              Format == WebGLImageConversion::DataFormatRG16F;
  };
  
  template <int Format>
  struct Is32bppFormat {
    STATIC_ONLY(Is32bppFormat);
    static const bool value =
        Format == WebGLImageConversion::DataFormatRGBA2_10_10_10 ||
        Format == WebGLImageConversion::DataFormatRGB5999 ||
        Format == WebGLImageConversion::DataFormatRGB10F11F11F;
  };
  
  template <int Format>
  struct Is16bppFormat {
    STATIC_ONLY(Is16bppFormat);
    static const bool value =
        Format == WebGLImageConversion::DataFormatRGBA5551 ||
        Format == WebGLImageConversion::DataFormatRGBA4444 ||
        Format == WebGLImageConversion::DataFormatRGB565;
  };
  
  template <int Format,
            bool IsInt8Format = IsInt8Format<Format>::value,
            bool IsUInt8Format = IsUInt8Format<Format>::value,
            bool IsInt16Format = IsInt16Format<Format>::value,
            bool IsUInt16Format = IsUInt16Format<Format>::value,
            bool IsInt32Format = IsInt32Format<Format>::value,
            bool IsUInt32Format = IsUInt32Format<Format>::value,
            bool IsFloat = IsFloatFormat<Format>::value,
            bool IsHalfFloat = IsHalfFloatFormat<Format>::value,
            bool Is16bpp = Is16bppFormat<Format>::value,
            bool Is32bpp = Is32bppFormat<Format>::value>
  struct DataTypeForFormat {
    STATIC_ONLY(DataTypeForFormat);
    typedef double Type;  // Use a type that's not used in unpack/pack.
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           true,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false> {
    STATIC_ONLY(DataTypeForFormat);
    typedef int8_t Type;
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           false,
                           true,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false> {
    STATIC_ONLY(DataTypeForFormat);
    typedef uint8_t Type;
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           false,
                           false,
                           true,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false> {
    STATIC_ONLY(DataTypeForFormat);
    typedef int16_t Type;
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           false,
                           false,
                           false,
                           true,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false> {
    STATIC_ONLY(DataTypeForFormat);
    typedef uint16_t Type;
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           false,
                           false,
                           false,
                           false,
                           true,
                           false,
                           false,
                           false,
                           false,
                           false> {
    STATIC_ONLY(DataTypeForFormat);
    typedef int32_t Type;
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           false,
                           false,
                           false,
                           false,
                           false,
                           true,
                           false,
                           false,
                           false,
                           false> {
    STATIC_ONLY(DataTypeForFormat);
    typedef uint32_t Type;
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           true,
                           false,
                           false,
                           false> {
    STATIC_ONLY(DataTypeForFormat);
    typedef float Type;
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           true,
                           false,
                           false> {
    STATIC_ONLY(DataTypeForFormat);
    typedef uint16_t Type;
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           true,
                           false> {
    STATIC_ONLY(DataTypeForFormat);
    typedef uint16_t Type;
  };
  
  template <int Format>
  struct DataTypeForFormat<Format,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           false,
                           true> {
    STATIC_ONLY(DataTypeForFormat);
    typedef uint32_t Type;
  };
  
  template <int Format>
  struct UsesFloatIntermediateFormat {
    STATIC_ONLY(UsesFloatIntermediateFormat);
    static const bool value =
        IsFloatFormat<Format>::value || IsHalfFloatFormat<Format>::value ||
        Format == WebGLImageConversion::DataFormatRGBA2_10_10_10 ||
        Format == WebGLImageConversion::DataFormatRGB10F11F11F ||
        Format == WebGLImageConversion::DataFormatRGB5999;
  };
  
  template <int Format>
  struct IntermediateFormat {
    STATIC_ONLY(IntermediateFormat);
    static const int value =
        UsesFloatIntermediateFormat<Format>::value
            ? WebGLImageConversion::DataFormatRGBA32F
            : IsInt32Format<Format>::value
                  ? WebGLImageConversion::DataFormatRGBA32_S
                  : IsUInt32Format<Format>::value
                        ? WebGLImageConversion::DataFormatRGBA32
                        : IsInt16Format<Format>::value
                              ? WebGLImageConversion::DataFormatRGBA16_S
                              : (IsUInt16Format<Format>::value ||
                                 Is32bppFormat<Format>::value)
                                    ? WebGLImageConversion::DataFormatRGBA16
                                    : IsInt8Format<Format>::value
                                          ? WebGLImageConversion::
                                                DataFormatRGBA8_S
                                          : WebGLImageConversion::DataFormatRGBA8;
  };
  
  unsigned TexelBytesForFormat(WebGLImageConversion::DataFormat format) {
    switch (format) {
      case WebGLImageConversion::DataFormatR8:
      case WebGLImageConversion::DataFormatR8_S:
      case WebGLImageConversion::DataFormatA8:
        return 1;
      case WebGLImageConversion::DataFormatRG8:
      case WebGLImageConversion::DataFormatRG8_S:
      case WebGLImageConversion::DataFormatRA8:
      case WebGLImageConversion::DataFormatAR8:
      case WebGLImageConversion::DataFormatRGBA5551:
      case WebGLImageConversion::DataFormatRGBA4444:
      case WebGLImageConversion::DataFormatRGB565:
      case WebGLImageConversion::DataFormatA16F:
      case WebGLImageConversion::DataFormatR16:
      case WebGLImageConversion::DataFormatR16_S:
      case WebGLImageConversion::DataFormatR16F:
      case WebGLImageConversion::DataFormatD16:
        return 2;
      case WebGLImageConversion::DataFormatRGB8:
      case WebGLImageConversion::DataFormatRGB8_S:
      case WebGLImageConversion::DataFormatBGR8:
        return 3;
      case WebGLImageConversion::DataFormatRGBA8:
      case WebGLImageConversion::DataFormatRGBA8_S:
      case WebGLImageConversion::DataFormatARGB8:
      case WebGLImageConversion::DataFormatABGR8:
      case WebGLImageConversion::DataFormatBGRA8:
      case WebGLImageConversion::DataFormatR32:
      case WebGLImageConversion::DataFormatR32_S:
      case WebGLImageConversion::DataFormatR32F:
      case WebGLImageConversion::DataFormatA32F:
      case WebGLImageConversion::DataFormatRA16F:
      case WebGLImageConversion::DataFormatRGBA2_10_10_10:
      case WebGLImageConversion::DataFormatRGB10F11F11F:
      case WebGLImageConversion::DataFormatRGB5999:
      case WebGLImageConversion::DataFormatRG16:
      case WebGLImageConversion::DataFormatRG16_S:
      case WebGLImageConversion::DataFormatRG16F:
      case WebGLImageConversion::DataFormatD32:
      case WebGLImageConversion::DataFormatD32F:
      case WebGLImageConversion::DataFormatDS24_8:
        return 4;
      case WebGLImageConversion::DataFormatRGB16:
      case WebGLImageConversion::DataFormatRGB16_S:
      case WebGLImageConversion::DataFormatRGB16F:
        return 6;
      case WebGLImageConversion::DataFormatRGBA16:
      case WebGLImageConversion::DataFormatRGBA16_S:
      case WebGLImageConversion::DataFormatRA32F:
      case WebGLImageConversion::DataFormatRGBA16F:
      case WebGLImageConversion::DataFormatRG32:
      case WebGLImageConversion::DataFormatRG32_S:
      case WebGLImageConversion::DataFormatRG32F:
        return 8;
      case WebGLImageConversion::DataFormatRGB32:
      case WebGLImageConversion::DataFormatRGB32_S:
      case WebGLImageConversion::DataFormatRGB32F:
        return 12;
      case WebGLImageConversion::DataFormatRGBA32:
      case WebGLImageConversion::DataFormatRGBA32_S:
      case WebGLImageConversion::DataFormatRGBA32F:
        return 16;
      default:
        return 0;
    }
  }
  
  /* END CODE SHARED WITH MOZILLA FIREFOX */
  
  class FormatConverter {
    STACK_ALLOCATED();
  
   public:
    FormatConverter(const IntRect& sourceDataSubRectangle,
                    int depth,
                    int unpackImageHeight,
                    const void* srcStart,
                    void* dstStart,
                    int srcStride,
                    int srcRowOffset,
                    int dstStride)
        : m_srcSubRectangle(sourceDataSubRectangle),
          m_depth(depth),
          m_unpackImageHeight(unpackImageHeight),
          m_srcStart(srcStart),
          m_dstStart(dstStart),
          m_srcStride(srcStride),
          m_srcRowOffset(srcRowOffset),
          m_dstStride(dstStride),
          m_success(false) {
      const unsigned MaxNumberOfComponents = 4;
      const unsigned MaxBytesPerComponent = 4;
      m_unpackedIntermediateSrcData = wrapArrayUnique(
          new uint8_t[m_srcSubRectangle.width() * MaxNumberOfComponents *
                      MaxBytesPerComponent]);
      ASSERT(m_unpackedIntermediateSrcData.get());
    }
  
    void convert(WebGLImageConversion::DataFormat srcFormat,
                 WebGLImageConversion::DataFormat dstFormat,
                 WebGLImageConversion::AlphaOp);
    bool Success() const { return m_success; }
  
   private:
    template <WebGLImageConversion::DataFormat SrcFormat>
    void convert(WebGLImageConversion::DataFormat dstFormat,
                 WebGLImageConversion::AlphaOp);
  
    template <WebGLImageConversion::DataFormat SrcFormat,
              WebGLImageConversion::DataFormat DstFormat>
    void convert(WebGLImageConversion::AlphaOp);
  
    template <WebGLImageConversion::DataFormat SrcFormat,
              WebGLImageConversion::DataFormat DstFormat,
              WebGLImageConversion::AlphaOp alphaOp>
    void convert();
  
    const IntRect& m_srcSubRectangle;
    const int m_depth;
    const int m_unpackImageHeight;
    const void* const m_srcStart;
    void* const m_dstStart;
    const int m_srcStride, m_srcRowOffset, m_dstStride;
    bool m_success;
    std::unique_ptr<uint8_t[]> m_unpackedIntermediateSrcData;
  };
  
  void FormatConverter::convert(WebGLImageConversion::DataFormat srcFormat,
                                WebGLImageConversion::DataFormat dstFormat,
                                WebGLImageConversion::AlphaOp alphaOp) {
  #define FORMATCONVERTER_CASE_SRCFORMAT(SrcFormat) \
    case SrcFormat:                                 \
      return convert<SrcFormat>(dstFormat, alphaOp);
  
    switch (srcFormat) {
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatRA8)
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatRA32F)
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatRGBA8)
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatARGB8)
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatABGR8)
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatAR8)
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatBGRA8)
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatRGBA5551)
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatRGBA4444)
      FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::DataFormatRGBA32F)
      FORMATCONVERTER_CASE_SRCFORMAT(
          WebGLImageConversion::DataFormatRGBA2_10_10_10)
      default:
        ASSERT_NOT_REACHED();
    }
  #undef FORMATCONVERTER_CASE_SRCFORMAT
  }
  
  template <WebGLImageConversion::DataFormat SrcFormat>
  void FormatConverter::convert(WebGLImageConversion::DataFormat dstFormat,
                                WebGLImageConversion::AlphaOp alphaOp) {
  #define FORMATCONVERTER_CASE_DSTFORMAT(DstFormat) \
    case DstFormat:                                 \
      return convert<SrcFormat, DstFormat>(alphaOp);
  
    switch (dstFormat) {
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatR8)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatR16F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatR32F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatA8)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatA16F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatA32F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRA8)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRA16F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRA32F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGB8)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGB565)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGB16F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGB32F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA8)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA5551)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA4444)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA16F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA32F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA8_S)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA16)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA16_S)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA32)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRGBA32_S)
      FORMATCONVERTER_CASE_DSTFORMAT(
          WebGLImageConversion::DataFormatRGBA2_10_10_10)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRG8)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRG16F)
      FORMATCONVERTER_CASE_DSTFORMAT(WebGLImageConversion::DataFormatRG32F)
      default:
        ASSERT_NOT_REACHED();
    }
  
  #undef FORMATCONVERTER_CASE_DSTFORMAT
  }
  
  template <WebGLImageConversion::DataFormat SrcFormat,
            WebGLImageConversion::DataFormat DstFormat>
  void FormatConverter::convert(WebGLImageConversion::AlphaOp alphaOp) {
  #define FORMATCONVERTER_CASE_ALPHAOP(alphaOp) \
    case alphaOp:                               \
      return convert<SrcFormat, DstFormat, alphaOp>();
  
    switch (alphaOp) {
      FORMATCONVERTER_CASE_ALPHAOP(WebGLImageConversion::AlphaDoNothing)
      FORMATCONVERTER_CASE_ALPHAOP(WebGLImageConversion::AlphaDoPremultiply)
      FORMATCONVERTER_CASE_ALPHAOP(WebGLImageConversion::AlphaDoUnmultiply)
      default:
        ASSERT_NOT_REACHED();
    }
  #undef FORMATCONVERTER_CASE_ALPHAOP
  }
  
  template <int Format>
  struct SupportsConversionFromDomElements {
    STATIC_ONLY(SupportsConversionFromDomElements);
    static const bool value =
        Format == WebGLImageConversion::DataFormatRGBA8 ||
        Format == WebGLImageConversion::DataFormatRGB8 ||
        Format == WebGLImageConversion::DataFormatRG8 ||
        Format == WebGLImageConversion::DataFormatRA8 ||
        Format == WebGLImageConversion::DataFormatR8 ||
        Format == WebGLImageConversion::DataFormatRGBA32F ||
        Format == WebGLImageConversion::DataFormatRGB32F ||
        Format == WebGLImageConversion::DataFormatRG32F ||
        Format == WebGLImageConversion::DataFormatRA32F ||
        Format == WebGLImageConversion::DataFormatR32F ||
        Format == WebGLImageConversion::DataFormatRGBA16F ||
        Format == WebGLImageConversion::DataFormatRGB16F ||
        Format == WebGLImageConversion::DataFormatRG16F ||
        Format == WebGLImageConversion::DataFormatRA16F ||
        Format == WebGLImageConversion::DataFormatR16F ||
        Format == WebGLImageConversion::DataFormatRGBA5551 ||
        Format == WebGLImageConversion::DataFormatRGBA4444 ||
        Format == WebGLImageConversion::DataFormatRGB565;
  };
  
  template <WebGLImageConversion::DataFormat SrcFormat,
            WebGLImageConversion::DataFormat DstFormat,
            WebGLImageConversion::AlphaOp alphaOp>
  void FormatConverter::convert() {
    // Many instantiations of this template function will never be entered, so we
    // try to return immediately in these cases to avoid generating useless code.
    if (SrcFormat == DstFormat &&
        alphaOp == WebGLImageConversion::AlphaDoNothing) {
      ASSERT_NOT_REACHED();
      return;
    }
    if (!IsFloatFormat<DstFormat>::value && IsFloatFormat<SrcFormat>::value) {
      ASSERT_NOT_REACHED();
      return;
    }
  
    // Only textures uploaded from DOM elements or ImageData can allow DstFormat
    // != SrcFormat.
    const bool srcFormatComesFromDOMElementOrImageData =
        WebGLImageConversion::srcFormatComeFromDOMElementOrImageData(SrcFormat);
    if (!srcFormatComesFromDOMElementOrImageData && SrcFormat != DstFormat) {
      ASSERT_NOT_REACHED();
      return;
    }
    // Likewise, only textures uploaded from DOM elements or ImageData can
    // possibly need to be unpremultiplied.
    if (!srcFormatComesFromDOMElementOrImageData &&
        alphaOp == WebGLImageConversion::AlphaDoUnmultiply) {
      ASSERT_NOT_REACHED();
      return;
    }
    if (srcFormatComesFromDOMElementOrImageData &&
        alphaOp == WebGLImageConversion::AlphaDoUnmultiply &&
        !SupportsConversionFromDomElements<DstFormat>::value) {
      ASSERT_NOT_REACHED();
      return;
    }
    if ((!HasAlpha(SrcFormat) || !HasColor(SrcFormat) || !HasColor(DstFormat)) &&
        alphaOp != WebGLImageConversion::AlphaDoNothing) {
      ASSERT_NOT_REACHED();
      return;
    }
    // If converting DOM element data to UNSIGNED_INT_5_9_9_9_REV or
    // UNSIGNED_INT_10F_11F_11F_REV, we should always switch to FLOAT instead to
    // avoid unpacking/packing these two types.
    if (srcFormatComesFromDOMElementOrImageData && SrcFormat != DstFormat &&
        (DstFormat == WebGLImageConversion::DataFormatRGB5999 ||
         DstFormat == WebGLImageConversion::DataFormatRGB10F11F11F)) {
      ASSERT_NOT_REACHED();
      return;
    }
  
    typedef typename DataTypeForFormat<SrcFormat>::Type SrcType;
    typedef typename DataTypeForFormat<DstFormat>::Type DstType;
    const int IntermFormat = IntermediateFormat<DstFormat>::value;
    typedef typename DataTypeForFormat<IntermFormat>::Type IntermType;
    const ptrdiff_t srcStrideInElements = m_srcStride / sizeof(SrcType);
    const ptrdiff_t dstStrideInElements = m_dstStride / sizeof(DstType);
    const bool trivialUnpack = SrcFormat == IntermFormat;
    const bool trivialPack = DstFormat == IntermFormat &&
                             alphaOp == WebGLImageConversion::AlphaDoNothing;
    ASSERT(!trivialUnpack || !trivialPack);
  
    const SrcType* srcRowStart =
        static_cast<const SrcType*>(static_cast<const void*>(
            static_cast<const uint8_t*>(m_srcStart) +
            ((m_srcStride * m_srcSubRectangle.y()) + m_srcRowOffset)));
  
    // If packing multiple images into a 3D texture, and flipY is true,
    // then the sub-rectangle is pointing at the start of the
    // "bottommost" of those images. Since the source pointer strides in
    // the positive direction, we need to back it up to point at the
    // last, or "topmost", of these images.
    if (m_dstStride < 0 && m_depth > 1) {
      srcRowStart -= (m_depth - 1) * srcStrideInElements * m_unpackImageHeight;
    }
  
    DstType* dstRowStart = static_cast<DstType*>(m_dstStart);
    if (trivialUnpack) {
      for (int d = 0; d < m_depth; ++d) {
        for (int i = 0; i < m_srcSubRectangle.height(); ++i) {
          pack<DstFormat, alphaOp>(srcRowStart, dstRowStart,
                                   m_srcSubRectangle.width());
          srcRowStart += srcStrideInElements;
          dstRowStart += dstStrideInElements;
        }
        srcRowStart += srcStrideInElements *
                       (m_unpackImageHeight - m_srcSubRectangle.height());
      }
    } else if (trivialPack) {
      for (int d = 0; d < m_depth; ++d) {
        for (int i = 0; i < m_srcSubRectangle.height(); ++i) {
          unpack<SrcFormat>(srcRowStart, dstRowStart, m_srcSubRectangle.width());
          srcRowStart += srcStrideInElements;
          dstRowStart += dstStrideInElements;
        }
        srcRowStart += srcStrideInElements *
                       (m_unpackImageHeight - m_srcSubRectangle.height());
      }
    } else {
      for (int d = 0; d < m_depth; ++d) {
        for (int i = 0; i < m_srcSubRectangle.height(); ++i) {
          unpack<SrcFormat>(srcRowStart, reinterpret_cast<IntermType*>(
                                             m_unpackedIntermediateSrcData.get()),
                            m_srcSubRectangle.width());
          pack<DstFormat, alphaOp>(
              reinterpret_cast<IntermType*>(m_unpackedIntermediateSrcData.get()),
              dstRowStart, m_srcSubRectangle.width());
          srcRowStart += srcStrideInElements;
          dstRowStart += dstStrideInElements;
        }
        srcRowStart += srcStrideInElements *
                       (m_unpackImageHeight - m_srcSubRectangle.height());
      }
    }
    m_success = true;
    return;
  }
  
  bool frameIsValid(const SkBitmap& frameBitmap) {
    return !frameBitmap.isNull() && !frameBitmap.empty() &&
           frameBitmap.colorType() == kN32_SkColorType;
  }
  
  }  // anonymous namespace
  
  WebGLImageConversion::PixelStoreParams::PixelStoreParams()
      : alignment(4),
        rowLength(0),
        imageHeight(0),
        skipPixels(0),
        skipRows(0),
        skipImages(0) {}
  
  bool WebGLImageConversion::computeFormatAndTypeParameters(
      GLenum format,
      GLenum type,
      unsigned* componentsPerPixel,
      unsigned* bytesPerComponent) {
    switch (format) {
      case GL_ALPHA:
      case GL_LUMINANCE:
      case GL_RED:
      case GL_RED_INTEGER:
      case GL_DEPTH_COMPONENT:
      case GL_DEPTH_STENCIL:  // Treat it as one component.
        *componentsPerPixel = 1;
        break;
      case GL_LUMINANCE_ALPHA:
      case GL_RG:
      case GL_RG_INTEGER:
        *componentsPerPixel = 2;
        break;
      case GL_RGB:
      case GL_RGB_INTEGER:
      case GL_SRGB_EXT:  // GL_EXT_sRGB
        *componentsPerPixel = 3;
        break;
      case GL_RGBA:
      case GL_RGBA_INTEGER:
      case GL_BGRA_EXT:        // GL_EXT_texture_format_BGRA8888
      case GL_SRGB_ALPHA_EXT:  // GL_EXT_sRGB
        *componentsPerPixel = 4;
        break;
      default:
        return false;
    }
    switch (type) {
      case GL_BYTE:
        *bytesPerComponent = sizeof(GLbyte);
        break;
      case GL_UNSIGNED_BYTE:
        *bytesPerComponent = sizeof(GLubyte);
        break;
      case GL_SHORT:
        *bytesPerComponent = sizeof(GLshort);
        break;
      case GL_UNSIGNED_SHORT:
        *bytesPerComponent = sizeof(GLushort);
        break;
      case GL_UNSIGNED_SHORT_5_6_5:
      case GL_UNSIGNED_SHORT_4_4_4_4:
      case GL_UNSIGNED_SHORT_5_5_5_1:
        *componentsPerPixel = 1;
        *bytesPerComponent = sizeof(GLushort);
        break;
      case GL_INT:
        *bytesPerComponent = sizeof(GLint);
        break;
      case GL_UNSIGNED_INT:
        *bytesPerComponent = sizeof(GLuint);
        break;
      case GL_UNSIGNED_INT_24_8_OES:
      case GL_UNSIGNED_INT_10F_11F_11F_REV:
      case GL_UNSIGNED_INT_5_9_9_9_REV:
      case GL_UNSIGNED_INT_2_10_10_10_REV:
        *componentsPerPixel = 1;
        *bytesPerComponent = sizeof(GLuint);
        break;
      case GL_FLOAT:  // OES_texture_float
        *bytesPerComponent = sizeof(GLfloat);
        break;
      case GL_HALF_FLOAT:
      case GL_HALF_FLOAT_OES:  // OES_texture_half_float
        *bytesPerComponent = sizeof(GLushort);
        break;
      default:
        return false;
    }
    return true;
  }
  
  GLenum WebGLImageConversion::computeImageSizeInBytes(
      GLenum format,
      GLenum type,
      GLsizei width,
      GLsizei height,
      GLsizei depth,
      const PixelStoreParams& params,
      unsigned* imageSizeInBytes,
      unsigned* paddingInBytes,
      unsigned* skipSizeInBytes) {
    ASSERT(imageSizeInBytes);
    ASSERT(params.alignment == 1 || params.alignment == 2 ||
           params.alignment == 4 || params.alignment == 8);
    ASSERT(params.rowLength >= 0 && params.imageHeight >= 0);
    ASSERT(params.skipPixels >= 0 && params.skipRows >= 0 &&
           params.skipImages >= 0);
    if (width < 0 || height < 0 || depth < 0)
      return GL_INVALID_VALUE;
    if (!width || !height || !depth) {
      *imageSizeInBytes = 0;
      if (paddingInBytes)
        *paddingInBytes = 0;
      if (skipSizeInBytes)
        *skipSizeInBytes = 0;
      return GL_NO_ERROR;
    }
  
    int rowLength = params.rowLength > 0 ? params.rowLength : width;
    int imageHeight = params.imageHeight > 0 ? params.imageHeight : height;
  
    unsigned bytesPerComponent, componentsPerPixel;
    if (!computeFormatAndTypeParameters(format, type, &bytesPerComponent,
                                        &componentsPerPixel))
      return GL_INVALID_ENUM;
    unsigned bytesPerGroup = bytesPerComponent * componentsPerPixel;
    CheckedNumeric<uint32_t> checkedValue = static_cast<uint32_t>(rowLength);
    checkedValue *= bytesPerGroup;
    if (!checkedValue.IsValid())
      return GL_INVALID_VALUE;
  
    unsigned lastRowSize;
    if (params.rowLength > 0 && params.rowLength != width) {
      CheckedNumeric<uint32_t> tmp = width;
      tmp *= bytesPerGroup;
      if (!tmp.IsValid())
        return GL_INVALID_VALUE;
      lastRowSize = tmp.ValueOrDie();
    } else {
      lastRowSize = checkedValue.ValueOrDie();
    }
  
    unsigned padding = 0;
    unsigned residual = checkedValue.ValueOrDie() % params.alignment;
    if (residual) {
      padding = params.alignment - residual;
      checkedValue += padding;
    }
    if (!checkedValue.IsValid())
      return GL_INVALID_VALUE;
    unsigned paddedRowSize = checkedValue.ValueOrDie();
  
    CheckedNumeric<uint32_t> rows = imageHeight;
    rows *= (depth - 1);
    // Last image is not affected by IMAGE_HEIGHT parameter.
    rows += height;
    if (!rows.IsValid())
      return GL_INVALID_VALUE;
    checkedValue *= (rows.ValueOrDie() - 1);
    // Last row is not affected by ROW_LENGTH parameter.
    checkedValue += lastRowSize;
    if (!checkedValue.IsValid())
      return GL_INVALID_VALUE;
    *imageSizeInBytes = checkedValue.ValueOrDie();
    if (paddingInBytes)
      *paddingInBytes = padding;
  
    CheckedNumeric<uint32_t> skipSize = 0;
    if (params.skipImages > 0) {
      CheckedNumeric<uint32_t> tmp = paddedRowSize;
      tmp *= imageHeight;
      tmp *= params.skipImages;
      if (!tmp.IsValid())
        return GL_INVALID_VALUE;
      skipSize += tmp.ValueOrDie();
    }
    if (params.skipRows > 0) {
      CheckedNumeric<uint32_t> tmp = paddedRowSize;
      tmp *= params.skipRows;
      if (!tmp.IsValid())
        return GL_INVALID_VALUE;
      skipSize += tmp.ValueOrDie();
    }
    if (params.skipPixels > 0) {
      CheckedNumeric<uint32_t> tmp = bytesPerGroup;
      tmp *= params.skipPixels;
      if (!tmp.IsValid())
        return GL_INVALID_VALUE;
      skipSize += tmp.ValueOrDie();
    }
    if (!skipSize.IsValid())
      return GL_INVALID_VALUE;
    if (skipSizeInBytes)
      *skipSizeInBytes = skipSize.ValueOrDie();
  
    checkedValue += skipSize.ValueOrDie();
    if (!checkedValue.IsValid())
      return GL_INVALID_VALUE;
    return GL_NO_ERROR;
  }
  
  WebGLImageConversion::ImageExtractor::ImageExtractor(
      Image* image,
      ImageHtmlDomSource imageHtmlDomSource,
      bool premultiplyAlpha,
      bool ignoreColorSpace) {
    m_image = image;
    m_imageHtmlDomSource = imageHtmlDomSource;
    extractImage(premultiplyAlpha, ignoreColorSpace);
  }
  
  void WebGLImageConversion::ImageExtractor::extractImage(bool premultiplyAlpha,
                                                          bool ignoreColorSpace) {
    ASSERT(!m_imagePixelLocker);
  
    if (!m_image)
      return;
  
    sk_sp<SkImage> skiaImage = m_image->imageForCurrentFrame();
    SkImageInfo info = skiaImage ? SkImageInfo::MakeN32Premul(m_image->width(),
                                                              m_image->height())
                                 : SkImageInfo::MakeUnknown();
    m_alphaOp = AlphaDoNothing;
    bool hasAlpha = skiaImage ? !skiaImage->isOpaque() : true;
  
    if ((!skiaImage || ignoreColorSpace || (hasAlpha && !premultiplyAlpha)) &&
        m_image->data()) {
      // Attempt to get raw unpremultiplied image data.
      std::unique_ptr<ImageDecoder> decoder(ImageDecoder::create(
          m_image->data(), true, ImageDecoder::AlphaNotPremultiplied,
          ignoreColorSpace ? ImageDecoder::ColorSpaceIgnored
                           : ImageDecoder::ColorSpaceApplied));
      if (!decoder || !decoder->frameCount())
        return;
      ImageFrame* frame = decoder->frameBufferAtIndex(0);
      if (!frame || frame->getStatus() != ImageFrame::FrameComplete)
        return;
      hasAlpha = frame->hasAlpha();
      SkBitmap bitmap = frame->bitmap();
      if (!frameIsValid(bitmap))
        return;
  
      // TODO(fmalita): Partial frames are not supported currently: only fully
      // decoded frames make it through.  We could potentially relax this and
      // use SkImage::MakeFromBitmap(bitmap) to make a copy.
      skiaImage = frame->finalizePixelsAndGetImage();
      info = bitmap.info();
  
      if (hasAlpha && premultiplyAlpha)
        m_alphaOp = AlphaDoPremultiply;
    } else if (!premultiplyAlpha && hasAlpha) {
      // 1. For texImage2D with HTMLVideoElment input, assume no PremultiplyAlpha
      //    had been applied and the alpha value for each pixel is 0xFF.  This is
      //    true at present; if it is changed in the future it will need
      //    adjustment accordingly.
      // 2. For texImage2D with HTMLCanvasElement input in which alpha is already
      //    premultiplied in this port, do AlphaDoUnmultiply if
      //    UNPACK_PREMULTIPLY_ALPHA_WEBGL is set to false.
      if (m_imageHtmlDomSource != HtmlDomVideo)
        m_alphaOp = AlphaDoUnmultiply;
    }
  
    if (!skiaImage)
      return;
  
    m_imageSourceFormat = SK_B32_SHIFT ? DataFormatRGBA8 : DataFormatBGRA8;
    m_imageSourceUnpackAlignment =
        0;  // FIXME: this seems to always be zero - why use at all?
  
    ASSERT(skiaImage->width() && skiaImage->height());
    m_imageWidth = skiaImage->width();
    m_imageHeight = skiaImage->height();
  
    // Fail if the image was downsampled because of memory limits.
    if (m_imageWidth != (unsigned)m_image->width() ||
        m_imageHeight != (unsigned)m_image->height())
      return;
  
    m_imagePixelLocker.emplace(std::move(skiaImage), info.alphaType(),
                               kN32_SkColorType);
  }
  
  unsigned WebGLImageConversion::getChannelBitsByFormat(GLenum format) {
    switch (format) {
      case GL_ALPHA:
        return ChannelAlpha;
      case GL_RED:
      case GL_RED_INTEGER:
      case GL_R8:
      case GL_R8_SNORM:
      case GL_R8UI:
      case GL_R8I:
      case GL_R16UI:
      case GL_R16I:
      case GL_R32UI:
      case GL_R32I:
      case GL_R16F:
      case GL_R32F:
        return ChannelRed;
      case GL_RG:
      case GL_RG_INTEGER:
      case GL_RG8:
      case GL_RG8_SNORM:
      case GL_RG8UI:
      case GL_RG8I:
      case GL_RG16UI:
      case GL_RG16I:
      case GL_RG32UI:
      case GL_RG32I:
      case GL_RG16F:
      case GL_RG32F:
        return ChannelRG;
      case GL_LUMINANCE:
        return ChannelRGB;
      case GL_LUMINANCE_ALPHA:
        return ChannelRGBA;
      case GL_RGB:
      case GL_RGB_INTEGER:
      case GL_RGB8:
      case GL_RGB8_SNORM:
      case GL_RGB8UI:
      case GL_RGB8I:
      case GL_RGB16UI:
      case GL_RGB16I:
      case GL_RGB32UI:
      case GL_RGB32I:
      case GL_RGB16F:
      case GL_RGB32F:
      case GL_RGB565:
      case GL_R11F_G11F_B10F:
      case GL_RGB9_E5:
      case GL_SRGB_EXT:
      case GL_SRGB8:
        return ChannelRGB;
      case GL_RGBA:
      case GL_RGBA_INTEGER:
      case GL_RGBA8:
      case GL_RGBA8_SNORM:
      case GL_RGBA8UI:
      case GL_RGBA8I:
      case GL_RGBA16UI:
      case GL_RGBA16I:
      case GL_RGBA32UI:
      case GL_RGBA32I:
      case GL_RGBA16F:
      case GL_RGBA32F:
      case GL_RGBA4:
      case GL_RGB5_A1:
      case GL_RGB10_A2:
      case GL_RGB10_A2UI:
      case GL_SRGB_ALPHA_EXT:
      case GL_SRGB8_ALPHA8:
        return ChannelRGBA;
      case GL_DEPTH_COMPONENT:
      case GL_DEPTH_COMPONENT16:
      case GL_DEPTH_COMPONENT24:
      case GL_DEPTH_COMPONENT32F:
        return ChannelDepth;
      case GL_STENCIL:
      case GL_STENCIL_INDEX8:
        return ChannelStencil;
      case GL_DEPTH_STENCIL:
      case GL_DEPTH24_STENCIL8:
      case GL_DEPTH32F_STENCIL8:
        return ChannelDepthStencil;
      default:
        return 0;
    }
  }
  
  bool WebGLImageConversion::packImageData(Image* image,
                                           const void* pixels,
                                           GLenum format,
                                           GLenum type,
                                           bool flipY,
                                           AlphaOp alphaOp,
                                           DataFormat sourceFormat,
                                           unsigned sourceImageWidth,
                                           unsigned sourceImageHeight,
                                           const IntRect& sourceImageSubRectangle,
                                           int depth,
                                           unsigned sourceUnpackAlignment,
                                           int unpackImageHeight,
                                           Vector<uint8_t>& data) {
    if (!pixels)
      return false;
  
    unsigned packedSize;
    // Output data is tightly packed (alignment == 1).
    PixelStoreParams params;
    params.alignment = 1;
    if (computeImageSizeInBytes(format, type, sourceImageSubRectangle.width(),
                                sourceImageSubRectangle.height(), depth, params,
                                &packedSize, 0, 0) != GL_NO_ERROR)
      return false;
    data.resize(packedSize);
  
    if (!packPixels(reinterpret_cast<const uint8_t*>(pixels), sourceFormat,
                    sourceImageWidth, sourceImageHeight, sourceImageSubRectangle,
                    depth, sourceUnpackAlignment, unpackImageHeight, format, type,
                    alphaOp, data.data(), flipY))
      return false;
    if (ImageObserver* observer = image->getImageObserver())
      observer->didDraw(image);
    return true;
  }
  
  bool WebGLImageConversion::extractImageData(
      const uint8_t* imageData,
      DataFormat sourceDataFormat,
      const IntSize& imageDataSize,
      const IntRect& sourceImageSubRectangle,
      int depth,
      int unpackImageHeight,
      GLenum format,
      GLenum type,
      bool flipY,
      bool premultiplyAlpha,
      Vector<uint8_t>& data) {
    if (!imageData)
      return false;
    int width = imageDataSize.width();
    int height = imageDataSize.height();
  
    unsigned packedSize;
    // Output data is tightly packed (alignment == 1).
    PixelStoreParams params;
    params.alignment = 1;
    if (computeImageSizeInBytes(format, type, sourceImageSubRectangle.width(),
                                sourceImageSubRectangle.height(), depth, params,
                                &packedSize, 0, 0) != GL_NO_ERROR)
      return false;
    data.resize(packedSize);
  
    if (!packPixels(imageData, sourceDataFormat, width, height,
                    sourceImageSubRectangle, depth, 0, unpackImageHeight, format,
                    type, premultiplyAlpha ? AlphaDoPremultiply : AlphaDoNothing,
                    data.data(), flipY))
      return false;
  
    return true;
  }
  
  bool WebGLImageConversion::extractTextureData(unsigned width,
                                                unsigned height,
                                                GLenum format,
                                                GLenum type,
                                                unsigned unpackAlignment,
                                                bool flipY,
                                                bool premultiplyAlpha,
                                                const void* pixels,
                                                Vector<uint8_t>& data) {
    // Assumes format, type, etc. have already been validated.
    DataFormat sourceDataFormat = getDataFormat(format, type);
  
    // Resize the output buffer.
    unsigned int componentsPerPixel, bytesPerComponent;
    if (!computeFormatAndTypeParameters(format, type, &componentsPerPixel,
                                        &bytesPerComponent))
      return false;
    unsigned bytesPerPixel = componentsPerPixel * bytesPerComponent;
    data.resize(width * height * bytesPerPixel);
  
    if (!packPixels(static_cast<const uint8_t*>(pixels), sourceDataFormat, width,
                    height, IntRect(0, 0, width, height), 1, unpackAlignment, 0,
                    format, type,
                    (premultiplyAlpha ? AlphaDoPremultiply : AlphaDoNothing),
                    data.data(), flipY))
      return false;
  
    return true;
  }
  
  bool WebGLImageConversion::packPixels(const uint8_t* sourceData,
                                        DataFormat sourceDataFormat,
                                        unsigned sourceDataWidth,
                                        unsigned sourceDataHeight,
                                        const IntRect& sourceDataSubRectangle,
                                        int depth,
                                        unsigned sourceUnpackAlignment,
                                        int unpackImageHeight,
                                        unsigned destinationFormat,
                                        unsigned destinationType,
                                        AlphaOp alphaOp,
                                        void* destinationData,
                                        bool flipY) {
    DCHECK_GE(depth, 1);
    if (unpackImageHeight == 0) {
      unpackImageHeight = sourceDataSubRectangle.height();
    }
    int validSrc = sourceDataWidth * TexelBytesForFormat(sourceDataFormat);
    int remainder =
        sourceUnpackAlignment ? (validSrc % sourceUnpackAlignment) : 0;
    int srcStride =
        remainder ? (validSrc + sourceUnpackAlignment - remainder) : validSrc;
    int srcRowOffset =
        sourceDataSubRectangle.x() * TexelBytesForFormat(sourceDataFormat);
  
    DataFormat dstDataFormat = getDataFormat(destinationFormat, destinationType);
    int dstStride =
        sourceDataSubRectangle.width() * TexelBytesForFormat(dstDataFormat);
    if (flipY) {
      destinationData =
          static_cast<uint8_t*>(destinationData) +
          dstStride * ((depth * sourceDataSubRectangle.height()) - 1);
      dstStride = -dstStride;
    }
    if (!HasAlpha(sourceDataFormat) || !HasColor(sourceDataFormat) ||
        !HasColor(dstDataFormat))
      alphaOp = AlphaDoNothing;
  
    if (sourceDataFormat == dstDataFormat && alphaOp == AlphaDoNothing) {
      const uint8_t* basePtr =
          sourceData + srcStride * sourceDataSubRectangle.y();
      const uint8_t* baseEnd =
          sourceData + srcStride * sourceDataSubRectangle.maxY();
  
      // If packing multiple images into a 3D texture, and flipY is true,
      // then the sub-rectangle is pointing at the start of the
      // "bottommost" of those images. Since the source pointer strides in
      // the positive direction, we need to back it up to point at the
      // last, or "topmost", of these images.
      if (flipY && depth > 1) {
        const ptrdiff_t distanceToTopImage =
            (depth - 1) * srcStride * unpackImageHeight;
        basePtr -= distanceToTopImage;
        baseEnd -= distanceToTopImage;
      }
  
      unsigned rowSize = (dstStride > 0) ? dstStride : -dstStride;
      uint8_t* dst = static_cast<uint8_t*>(destinationData);
  
      for (int i = 0; i < depth; ++i) {
        const uint8_t* ptr = basePtr;
        const uint8_t* ptrEnd = baseEnd;
        while (ptr < ptrEnd) {
          memcpy(dst, ptr + srcRowOffset, rowSize);
          ptr += srcStride;
          dst += dstStride;
        }
        basePtr += unpackImageHeight * srcStride;
        baseEnd += unpackImageHeight * srcStride;
      }
      return true;
    }
  
    FormatConverter converter(sourceDataSubRectangle, depth, unpackImageHeight,
                              sourceData, destinationData, srcStride,
                              srcRowOffset, dstStride);
    converter.convert(sourceDataFormat, dstDataFormat, alphaOp);
    if (!converter.Success())
      return false;
    return true;
  }
  
  void WebGLImageConversion::unpackPixels(const uint16_t* sourceData,
                                          DataFormat sourceDataFormat,
                                          unsigned pixelsPerRow,
                                          uint8_t* destinationData) {
    switch (sourceDataFormat) {
      case DataFormatRGBA4444: {
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA4444>::Type SrcType;
        const SrcType* srcRowStart = static_cast<const SrcType*>(sourceData);
        unpack<WebGLImageConversion::DataFormatRGBA4444>(
            srcRowStart, destinationData, pixelsPerRow);
      } break;
      case DataFormatRGBA5551: {
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA5551>::Type SrcType;
        const SrcType* srcRowStart = static_cast<const SrcType*>(sourceData);
        unpack<WebGLImageConversion::DataFormatRGBA5551>(
            srcRowStart, destinationData, pixelsPerRow);
      } break;
      case DataFormatBGRA8: {
        const uint8_t* psrc = (const uint8_t*)sourceData;
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatBGRA8>::Type SrcType;
        const SrcType* srcRowStart = static_cast<const SrcType*>(psrc);
        unpack<WebGLImageConversion::DataFormatBGRA8>(
            srcRowStart, destinationData, pixelsPerRow);
      } break;
      default:
        break;
    }
  }
  
  void WebGLImageConversion::packPixels(const uint8_t* sourceData,
                                        DataFormat sourceDataFormat,
                                        unsigned pixelsPerRow,
                                        uint8_t* destinationData) {
    switch (sourceDataFormat) {
      case DataFormatRA8: {
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA8>::Type SrcType;
        const SrcType* srcRowStart = static_cast<const SrcType*>(sourceData);
        pack<WebGLImageConversion::DataFormatRA8,
             WebGLImageConversion::AlphaDoUnmultiply>(
            srcRowStart, destinationData, pixelsPerRow);
      } break;
      case DataFormatR8: {
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA8>::Type SrcType;
        const SrcType* srcRowStart = static_cast<const SrcType*>(sourceData);
        pack<WebGLImageConversion::DataFormatR8,
             WebGLImageConversion::AlphaDoUnmultiply>(
            srcRowStart, destinationData, pixelsPerRow);
      } break;
      case DataFormatRGBA8: {
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA8>::Type SrcType;
        const SrcType* srcRowStart = static_cast<const SrcType*>(sourceData);
        pack<WebGLImageConversion::DataFormatRGBA8,
             WebGLImageConversion::AlphaDoUnmultiply>(
            srcRowStart, destinationData, pixelsPerRow);
      } break;
      case DataFormatRGBA4444: {
        uint16_t* pdst = (uint16_t*)destinationData;
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA8>::Type SrcType;
        const SrcType* srcRowStart = static_cast<const SrcType*>(sourceData);
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA4444>::Type DstType;
        DstType* dstRowStart = static_cast<DstType*>(pdst);
        pack<WebGLImageConversion::DataFormatRGBA4444,
             WebGLImageConversion::AlphaDoNothing>(srcRowStart, dstRowStart,
                                                   pixelsPerRow);
      } break;
      case DataFormatRGBA5551: {
        uint16_t* pdst = (uint16_t*)destinationData;
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA8>::Type SrcType;
        const SrcType* srcRowStart = static_cast<const SrcType*>(sourceData);
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA5551>::Type DstType;
        DstType* dstRowStart = static_cast<DstType*>(pdst);
        pack<WebGLImageConversion::DataFormatRGBA5551,
             WebGLImageConversion::AlphaDoNothing>(srcRowStart, dstRowStart,
                                                   pixelsPerRow);
      } break;
      case DataFormatRGB565: {
        uint16_t* pdst = (uint16_t*)destinationData;
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGBA8>::Type SrcType;
        const SrcType* srcRowStart = static_cast<const SrcType*>(sourceData);
        typedef typename DataTypeForFormat<
            WebGLImageConversion::DataFormatRGB565>::Type DstType;
        DstType* dstRowStart = static_cast<DstType*>(pdst);
        pack<WebGLImageConversion::DataFormatRGB565,
             WebGLImageConversion::AlphaDoNothing>(srcRowStart, dstRowStart,
                                                   pixelsPerRow);
      } break;
      default:
        break;
    }
  }
  
  }  // namespace blink
  