static void TexImage2DHelper(GLContext *gl, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei stride, GLint pixelsize, GLint border, GLenum format, GLenum type, const GLvoid *pixels) { if (gl->IsGLES2()) { NS_ASSERTION(format == (GLenum)internalformat, "format and internalformat not the same for glTexImage2D on GLES2"); if (!CanUploadNonPowerOfTwo(gl) && (stride != width * pixelsize || !gfx::IsPowerOfTwo(width) || !gfx::IsPowerOfTwo(height))) { // Pad out texture width and height to the next power of two // as we don't support/want non power of two texture uploads GLsizei paddedWidth = gfx::NextPowerOfTwo(width); GLsizei paddedHeight = gfx::NextPowerOfTwo(height); GLvoid* paddedPixels = new unsigned char[paddedWidth * paddedHeight * pixelsize]; // Pad out texture data to be in a POT sized buffer for uploading to // a POT sized texture CopyAndPadTextureData(pixels, paddedPixels, width, height, paddedWidth, paddedHeight, stride, pixelsize); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, std::min(GetAddressAlignment((ptrdiff_t)paddedPixels), GetAddressAlignment((ptrdiff_t)paddedWidth * pixelsize))); gl->fTexImage2D(target, border, internalformat, paddedWidth, paddedHeight, border, format, type, paddedPixels); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); delete[] static_cast<unsigned char*>(paddedPixels); return; } if (stride == width * pixelsize) { gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, std::min(GetAddressAlignment((ptrdiff_t)pixels), GetAddressAlignment((ptrdiff_t)stride))); gl->fTexImage2D(target, border, internalformat, width, height, border, format, type, pixels); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); } else { // Use GLES-specific workarounds for GL_UNPACK_ROW_LENGTH; these are // implemented in TexSubImage2D. gl->fTexImage2D(target, border, internalformat, width, height, border, format, type, nullptr); TexSubImage2DHelper(gl, target, level, 0, 0, width, height, stride, pixelsize, format, type, pixels); } } else { // desktop GL (non-ES) path gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, std::min(GetAddressAlignment((ptrdiff_t)pixels), GetAddressAlignment((ptrdiff_t)stride))); int rowLength = stride/pixelsize; gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); gl->fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); } }
SurfaceFormat UploadImageDataToTexture(GLContext* gl, unsigned char* aData, int32_t aStride, gfxImageFormat aFormat, const nsIntRegion& aDstRegion, GLuint& aTexture, bool aOverwrite, bool aPixelBuffer, GLenum aTextureUnit, GLenum aTextureTarget) { bool textureInited = aOverwrite ? false : true; gl->MakeCurrent(); gl->fActiveTexture(aTextureUnit); if (!aTexture) { gl->fGenTextures(1, &aTexture); gl->fBindTexture(aTextureTarget, aTexture); gl->fTexParameteri(aTextureTarget, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR); gl->fTexParameteri(aTextureTarget, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR); gl->fTexParameteri(aTextureTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); gl->fTexParameteri(aTextureTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); textureInited = false; } else { gl->fBindTexture(aTextureTarget, aTexture); } nsIntRegion paintRegion; if (!textureInited) { paintRegion = nsIntRegion(aDstRegion.GetBounds()); } else { paintRegion = aDstRegion; } GLenum format; GLenum internalFormat; GLenum type; int32_t pixelSize = gfxASurface::BytePerPixelFromFormat(aFormat); SurfaceFormat surfaceFormat; MOZ_ASSERT(gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA || gl->GetPreferredARGB32Format() == LOCAL_GL_RGBA); switch (aFormat) { case gfxImageFormatARGB32: if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { format = LOCAL_GL_BGRA; surfaceFormat = gfx::FORMAT_R8G8B8A8; type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; } else { format = LOCAL_GL_RGBA; surfaceFormat = gfx::FORMAT_B8G8R8A8; type = LOCAL_GL_UNSIGNED_BYTE; } internalFormat = LOCAL_GL_RGBA; break; case gfxImageFormatRGB24: // Treat RGB24 surfaces as RGBA32 except for the surface // format used. if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { format = LOCAL_GL_BGRA; surfaceFormat = gfx::FORMAT_R8G8B8X8; type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; } else { format = LOCAL_GL_RGBA; surfaceFormat = gfx::FORMAT_B8G8R8X8; type = LOCAL_GL_UNSIGNED_BYTE; } internalFormat = LOCAL_GL_RGBA; break; case gfxImageFormatRGB16_565: internalFormat = format = LOCAL_GL_RGB; type = LOCAL_GL_UNSIGNED_SHORT_5_6_5; surfaceFormat = gfx::FORMAT_R5G6B5; break; case gfxImageFormatA8: internalFormat = format = LOCAL_GL_LUMINANCE; type = LOCAL_GL_UNSIGNED_BYTE; // We don't have a specific luminance shader surfaceFormat = gfx::FORMAT_A8; break; default: NS_ASSERTION(false, "Unhandled image surface format!"); format = 0; type = 0; surfaceFormat = gfx::FORMAT_UNKNOWN; } nsIntRegionRectIterator iter(paintRegion); const nsIntRect *iterRect; // Top left point of the region's bounding rectangle. nsIntPoint topLeft = paintRegion.GetBounds().TopLeft(); while ((iterRect = iter.Next())) { // The inital data pointer is at the top left point of the region's // bounding rectangle. We need to find the offset of this rect // within the region and adjust the data pointer accordingly. unsigned char *rectData = aData + DataOffset(iterRect->TopLeft() - topLeft, aStride, aFormat); NS_ASSERTION(textureInited || (iterRect->x == 0 && iterRect->y == 0), "Must be uploading to the origin when we don't have an existing texture"); if (textureInited && CanUploadSubTextures(gl)) { TexSubImage2DHelper(gl, aTextureTarget, 0, iterRect->x, iterRect->y, iterRect->width, iterRect->height, aStride, pixelSize, format, type, rectData); } else { TexImage2DHelper(gl, aTextureTarget, 0, internalFormat, iterRect->width, iterRect->height, aStride, pixelSize, 0, format, type, rectData); } } return surfaceFormat; }
SurfaceFormat UploadImageDataToTexture(GLContext* gl, unsigned char* aData, int32_t aStride, SurfaceFormat aFormat, const nsIntRegion& aDstRegion, GLuint aTexture, const gfx::IntSize& aSize, size_t* aOutUploadSize, bool aNeedInit, GLenum aTextureUnit, GLenum aTextureTarget) { gl->MakeCurrent(); gl->fActiveTexture(aTextureUnit); gl->fBindTexture(aTextureTarget, aTexture); GLenum format = 0; GLenum internalFormat = 0; GLenum type = 0; int32_t pixelSize = BytesPerPixel(aFormat); SurfaceFormat surfaceFormat = gfx::SurfaceFormat::UNKNOWN; MOZ_ASSERT(gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA || gl->GetPreferredARGB32Format() == LOCAL_GL_RGBA); switch (aFormat) { case SurfaceFormat::B8G8R8A8: if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { format = LOCAL_GL_BGRA; surfaceFormat = SurfaceFormat::R8G8B8A8; type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; } else { format = LOCAL_GL_RGBA; surfaceFormat = SurfaceFormat::B8G8R8A8; type = LOCAL_GL_UNSIGNED_BYTE; } internalFormat = LOCAL_GL_RGBA; break; case SurfaceFormat::B8G8R8X8: // Treat BGRX surfaces as BGRA except for the surface // format used. if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { format = LOCAL_GL_BGRA; surfaceFormat = SurfaceFormat::R8G8B8X8; type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; } else { format = LOCAL_GL_RGBA; surfaceFormat = SurfaceFormat::B8G8R8X8; type = LOCAL_GL_UNSIGNED_BYTE; } internalFormat = LOCAL_GL_RGBA; break; case SurfaceFormat::R8G8B8A8: if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { // Upload our RGBA as BGRA, but store that the uploaded format is // BGRA. (sample from R to get B) format = LOCAL_GL_BGRA; type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; surfaceFormat = SurfaceFormat::B8G8R8A8; } else { format = LOCAL_GL_RGBA; type = LOCAL_GL_UNSIGNED_BYTE; surfaceFormat = SurfaceFormat::R8G8B8A8; } internalFormat = LOCAL_GL_RGBA; break; case SurfaceFormat::R8G8B8X8: // Treat RGBX surfaces as RGBA except for the surface // format used. if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) { format = LOCAL_GL_BGRA; type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; surfaceFormat = SurfaceFormat::B8G8R8X8; } else { format = LOCAL_GL_RGBA; type = LOCAL_GL_UNSIGNED_BYTE; surfaceFormat = SurfaceFormat::R8G8B8X8; } internalFormat = LOCAL_GL_RGBA; break; case SurfaceFormat::R5G6B5_UINT16: internalFormat = format = LOCAL_GL_RGB; type = LOCAL_GL_UNSIGNED_SHORT_5_6_5; surfaceFormat = SurfaceFormat::R5G6B5_UINT16; break; case SurfaceFormat::A8: internalFormat = format = LOCAL_GL_LUMINANCE; type = LOCAL_GL_UNSIGNED_BYTE; // We don't have a specific luminance shader surfaceFormat = SurfaceFormat::A8; break; default: NS_ASSERTION(false, "Unhandled image surface format!"); } if (aOutUploadSize) { *aOutUploadSize = 0; } if (aNeedInit || !CanUploadSubTextures(gl)) { // If the texture needs initialized, or we are unable to // upload sub textures, then initialize and upload the entire // texture. TexImage2DHelper(gl, aTextureTarget, 0, internalFormat, aSize.width, aSize.height, aStride, pixelSize, 0, format, type, aData); if (aOutUploadSize && aNeedInit) { uint32_t texelSize = GetBytesPerTexel(internalFormat, type); size_t numTexels = size_t(aSize.width) * size_t(aSize.height); *aOutUploadSize += texelSize * numTexels; } } else { // Upload each rect in the region to the texture for (auto iter = aDstRegion.RectIter(); !iter.Done(); iter.Next()) { const IntRect& rect = iter.Get(); const unsigned char* rectData = aData + DataOffset(rect.TopLeft(), aStride, aFormat); TexSubImage2DHelper(gl, aTextureTarget, 0, rect.x, rect.y, rect.Width(), rect.Height(), aStride, pixelSize, format, type, rectData); } } return surfaceFormat; }