1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "GLReadTexImageHelper.h"
11 #include "GLContext.h"
12 #include "OGLShaderProgram.h"
13 #include "ScopedGLHelpers.h"
14 #include "gfx2DGlue.h"
17 #include "mozilla/gfx/2D.h"
18 #include "mozilla/gfx/Swizzle.h"
23 using namespace mozilla::gfx
;
25 GLReadTexImageHelper::GLReadTexImageHelper(GLContext
* gl
) : mGL(gl
) {
32 GLReadTexImageHelper::~GLReadTexImageHelper() {
33 if (!mGL
->MakeCurrent()) return;
35 mGL
->fDeleteProgram(mPrograms
[0]);
36 mGL
->fDeleteProgram(mPrograms
[1]);
37 mGL
->fDeleteProgram(mPrograms
[2]);
38 mGL
->fDeleteProgram(mPrograms
[3]);
41 static const GLchar readTextureImageVS
[] =
42 "attribute vec2 aVertex;\n"
43 "attribute vec2 aTexCoord;\n"
44 "varying vec2 vTexCoord;\n"
45 "void main() { gl_Position = vec4(aVertex, 0, 1); vTexCoord = aTexCoord; }";
47 static const GLchar readTextureImageFS_TEXTURE_2D
[] =
49 "precision mediump float;\n"
51 "varying vec2 vTexCoord;\n"
52 "uniform sampler2D uTexture;\n"
53 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
55 static const GLchar readTextureImageFS_TEXTURE_2D_BGRA
[] =
57 "precision mediump float;\n"
59 "varying vec2 vTexCoord;\n"
60 "uniform sampler2D uTexture;\n"
61 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }";
63 static const GLchar readTextureImageFS_TEXTURE_EXTERNAL
[] =
64 "#extension GL_OES_EGL_image_external : require\n"
66 "precision mediump float;\n"
68 "varying vec2 vTexCoord;\n"
69 "uniform samplerExternalOES uTexture;\n"
70 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
72 static const GLchar readTextureImageFS_TEXTURE_RECTANGLE
[] =
73 "#extension GL_ARB_texture_rectangle\n"
75 "precision mediump float;\n"
77 "varying vec2 vTexCoord;\n"
78 "uniform sampler2DRect uTexture;\n"
79 "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }";
81 GLuint
GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget
,
84 const GLchar
* readTextureImageFS
= nullptr;
85 if (aTextureTarget
== LOCAL_GL_TEXTURE_2D
) {
86 if (aConfig
& mozilla::layers::ENABLE_TEXTURE_RB_SWAP
) {
87 // Need to swizzle R/B.
88 readTextureImageFS
= readTextureImageFS_TEXTURE_2D_BGRA
;
91 readTextureImageFS
= readTextureImageFS_TEXTURE_2D
;
94 } else if (aTextureTarget
== LOCAL_GL_TEXTURE_EXTERNAL
) {
95 readTextureImageFS
= readTextureImageFS_TEXTURE_EXTERNAL
;
97 } else if (aTextureTarget
== LOCAL_GL_TEXTURE_RECTANGLE
) {
98 readTextureImageFS
= readTextureImageFS_TEXTURE_RECTANGLE
;
102 /* This might be overkill, but assure that we don't access out-of-bounds */
103 MOZ_ASSERT((size_t)variant
< ArrayLength(mPrograms
));
104 if (!mPrograms
[variant
]) {
105 GLuint vs
= mGL
->fCreateShader(LOCAL_GL_VERTEX_SHADER
);
106 const GLchar
* vsSourcePtr
= &readTextureImageVS
[0];
107 mGL
->fShaderSource(vs
, 1, &vsSourcePtr
, nullptr);
108 mGL
->fCompileShader(vs
);
110 GLuint fs
= mGL
->fCreateShader(LOCAL_GL_FRAGMENT_SHADER
);
111 mGL
->fShaderSource(fs
, 1, &readTextureImageFS
, nullptr);
112 mGL
->fCompileShader(fs
);
114 GLuint program
= mGL
->fCreateProgram();
115 mGL
->fAttachShader(program
, vs
);
116 mGL
->fAttachShader(program
, fs
);
117 mGL
->fBindAttribLocation(program
, 0, "aVertex");
118 mGL
->fBindAttribLocation(program
, 1, "aTexCoord");
119 mGL
->fLinkProgram(program
);
122 mGL
->fGetProgramiv(program
, LOCAL_GL_LINK_STATUS
, &success
);
125 mGL
->fDeleteProgram(program
);
129 mGL
->fDeleteShader(vs
);
130 mGL
->fDeleteShader(fs
);
132 mPrograms
[variant
] = program
;
135 return mPrograms
[variant
];
138 bool GLReadTexImageHelper::DidGLErrorOccur(const char* str
) {
139 GLenum error
= mGL
->fGetError();
140 if (error
!= LOCAL_GL_NO_ERROR
) {
141 printf_stderr("GL ERROR: %s %s\n",
142 GLContext::GLErrorToString(error
).c_str(), str
);
149 bool GetActualReadFormats(GLContext
* gl
, GLenum destFormat
, GLenum destType
,
150 GLenum
* out_readFormat
, GLenum
* out_readType
) {
151 MOZ_ASSERT(out_readFormat
);
152 MOZ_ASSERT(out_readType
);
154 if (destFormat
== LOCAL_GL_RGBA
&& destType
== LOCAL_GL_UNSIGNED_BYTE
) {
155 *out_readFormat
= destFormat
;
156 *out_readType
= destType
;
160 bool fallback
= true;
162 GLenum auxFormat
= 0;
165 gl
->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT
,
167 gl
->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE
, (GLint
*)&auxType
);
169 if (destFormat
== auxFormat
&& destType
== auxType
) {
173 switch (destFormat
) {
175 if (destType
== LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV
) fallback
= false;
178 case LOCAL_GL_BGRA
: {
179 if (destType
== LOCAL_GL_UNSIGNED_BYTE
||
180 destType
== LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
) {
189 *out_readFormat
= LOCAL_GL_RGBA
;
190 *out_readType
= LOCAL_GL_UNSIGNED_BYTE
;
193 *out_readFormat
= destFormat
;
194 *out_readType
= destType
;
199 void SwapRAndBComponents(DataSourceSurface
* surf
) {
200 DataSourceSurface::MappedSurface map
;
201 if (!surf
->Map(DataSourceSurface::MapType::READ_WRITE
, &map
)) {
202 MOZ_ASSERT(false, "SwapRAndBComponents: Failed to map surface.");
205 MOZ_ASSERT(map
.mStride
>= 0);
207 const size_t rowBytes
= surf
->GetSize().width
* 4;
208 const size_t rowHole
= map
.mStride
- rowBytes
;
210 uint8_t* row
= map
.mData
;
213 "SwapRAndBComponents: Failed to get data from"
214 " DataSourceSurface.");
219 const size_t rows
= surf
->GetSize().height
;
220 for (size_t i
= 0; i
< rows
; i
++) {
221 const uint8_t* rowEnd
= row
+ rowBytes
;
223 while (row
!= rowEnd
) {
224 std::swap(row
[0], row
[2]);
234 static int CalcRowStride(int width
, int pixelSize
, int alignment
) {
235 MOZ_ASSERT(alignment
);
237 int rowStride
= width
* pixelSize
;
238 if (rowStride
% alignment
) { // Extra at the end of the line?
239 int alignmentCount
= rowStride
/ alignment
;
240 rowStride
= (alignmentCount
+ 1) * alignment
;
245 static int GuessAlignment(int width
, int pixelSize
, int rowStride
) {
246 int alignment
= 8; // Max GLES allows.
247 while (CalcRowStride(width
, pixelSize
, alignment
) != rowStride
) {
250 NS_WARNING("Bad alignment for GLES. Will use temp surf for readback.");
257 void ReadPixelsIntoBuffer(GLContext
* gl
, uint8_t* aData
, int32_t aStride
,
258 const IntSize
& aSize
, SurfaceFormat aFormat
) {
260 MOZ_ASSERT(aSize
.width
!= 0);
261 MOZ_ASSERT(aSize
.height
!= 0);
264 aFormat
== SurfaceFormat::B8G8R8A8
|| aFormat
== SurfaceFormat::R8G8B8A8
;
271 case SurfaceFormat::B8G8R8A8
:
272 case SurfaceFormat::B8G8R8X8
:
273 // Needs host (little) endian ARGB.
274 destFormat
= LOCAL_GL_BGRA
;
275 destType
= LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
;
277 case SurfaceFormat::R8G8B8A8
:
278 case SurfaceFormat::R8G8B8X8
:
279 // Needs host (little) endian ABGR.
280 destFormat
= LOCAL_GL_RGBA
;
281 destType
= LOCAL_GL_UNSIGNED_BYTE
;
283 case SurfaceFormat::R5G6B5_UINT16
:
284 destFormat
= LOCAL_GL_RGB
;
285 destType
= LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV
;
288 MOZ_CRASH("GFX: Bad format, read pixels.");
290 destPixelSize
= BytesPerPixel(aFormat
);
292 MOZ_ASSERT(aSize
.width
* destPixelSize
<= aStride
);
294 GLenum readFormat
= destFormat
;
295 GLenum readType
= destType
;
297 !GetActualReadFormats(gl
, destFormat
, destType
, &readFormat
, &readType
);
299 RefPtr
<DataSourceSurface
> tempSurf
;
300 Maybe
<DataSourceSurface::ScopedMap
> tempMap
;
301 uint8_t* data
= aData
;
302 SurfaceFormat readFormatGFX
;
304 int readAlignment
= GuessAlignment(aSize
.width
, destPixelSize
, aStride
);
305 if (!readAlignment
) {
306 needsTempSurf
= true;
309 if (GLContext::ShouldSpew()) {
311 "Needing intermediary surface for ReadPixels. This will be slow!");
314 switch (readFormat
) {
315 case LOCAL_GL_RGBA
: {
317 hasAlpha
? SurfaceFormat::R8G8B8A8
: SurfaceFormat::R8G8B8X8
;
320 case LOCAL_GL_BGRA
: {
322 hasAlpha
? SurfaceFormat::B8G8R8A8
: SurfaceFormat::B8G8R8X8
;
326 MOZ_ASSERT(destPixelSize
== 2);
327 MOZ_ASSERT(readType
== LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV
);
328 readFormatGFX
= SurfaceFormat::R5G6B5_UINT16
;
332 MOZ_CRASH("GFX: Bad read format, read format.");
337 case LOCAL_GL_UNSIGNED_BYTE
: {
338 MOZ_ASSERT(readFormat
== LOCAL_GL_RGBA
);
342 case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
: {
343 MOZ_ASSERT(readFormat
== LOCAL_GL_BGRA
);
347 case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV
: {
348 MOZ_ASSERT(readFormat
== LOCAL_GL_RGB
);
353 MOZ_CRASH("GFX: Bad read type, read type.");
357 int32_t stride
= aSize
.width
* BytesPerPixel(readFormatGFX
);
358 tempSurf
= Factory::CreateDataSourceSurfaceWithStride(aSize
, readFormatGFX
,
360 if (NS_WARN_IF(!tempSurf
)) {
364 tempMap
.emplace(tempSurf
, DataSourceSurface::READ_WRITE
);
365 if (NS_WARN_IF(!tempMap
->IsMapped())) {
369 data
= tempMap
->GetData();
372 MOZ_ASSERT(readAlignment
);
373 MOZ_ASSERT(reinterpret_cast<uintptr_t>(data
) % readAlignment
== 0);
375 GLsizei width
= aSize
.width
;
376 GLsizei height
= aSize
.height
;
379 ScopedPackState
safePackState(gl
);
380 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, readAlignment
);
382 gl
->fReadPixels(0, 0, width
, height
, readFormat
, readType
, data
);
386 SwizzleData(tempMap
->GetData(), tempMap
->GetStride(), readFormatGFX
, aData
,
387 aStride
, aFormat
, aSize
);
391 void ReadPixelsIntoDataSurface(GLContext
* gl
, DataSourceSurface
* dest
) {
394 DataSourceSurface::ScopedMap
map(dest
, DataSourceSurface::WRITE
);
395 ReadPixelsIntoBuffer(gl
, map
.GetData(), map
.GetStride(), dest
->GetSize(),
399 already_AddRefed
<gfx::DataSourceSurface
> YInvertImageSurface(
400 gfx::DataSourceSurface
* aSurf
, uint32_t aStride
) {
401 RefPtr
<DataSourceSurface
> temp
= Factory::CreateDataSourceSurfaceWithStride(
402 aSurf
->GetSize(), aSurf
->GetFormat(), aStride
);
403 if (NS_WARN_IF(!temp
)) {
407 DataSourceSurface::MappedSurface map
;
408 if (!temp
->Map(DataSourceSurface::MapType::WRITE
, &map
)) {
412 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
413 BackendType::CAIRO
, map
.mData
, temp
->GetSize(), map
.mStride
,
420 dt
->SetTransform(Matrix::Scaling(1.0, -1.0) *
421 Matrix::Translation(0.0, aSurf
->GetSize().height
));
422 Rect
rect(0, 0, aSurf
->GetSize().width
, aSurf
->GetSize().height
);
424 aSurf
, rect
, rect
, DrawSurfaceOptions(),
425 DrawOptions(1.0, CompositionOp::OP_SOURCE
, AntialiasMode::NONE
));
427 return temp
.forget();
430 already_AddRefed
<DataSourceSurface
> ReadBackSurface(GLContext
* gl
,
433 SurfaceFormat aFormat
) {
435 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
);
436 gl
->fBindTexture(LOCAL_GL_TEXTURE_2D
, aTexture
);
439 gl
->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D
, 0, LOCAL_GL_TEXTURE_WIDTH
,
441 gl
->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D
, 0, LOCAL_GL_TEXTURE_HEIGHT
,
444 RefPtr
<DataSourceSurface
> surf
= Factory::CreateDataSourceSurfaceWithStride(
445 size
, SurfaceFormat::B8G8R8A8
,
446 GetAlignedStride
<4>(size
.width
, BytesPerPixel(SurfaceFormat::B8G8R8A8
)));
448 if (NS_WARN_IF(!surf
)) {
452 uint32_t currentPackAlignment
= 0;
453 gl
->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT
, (GLint
*)¤tPackAlignment
);
454 if (currentPackAlignment
!= 4) {
455 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, 4);
458 DataSourceSurface::ScopedMap
map(surf
, DataSourceSurface::READ
);
459 gl
->fGetTexImage(LOCAL_GL_TEXTURE_2D
, 0, LOCAL_GL_RGBA
,
460 LOCAL_GL_UNSIGNED_BYTE
, map
.GetData());
462 if (currentPackAlignment
!= 4) {
463 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, currentPackAlignment
);
466 if (aFormat
== SurfaceFormat::R8G8B8A8
||
467 aFormat
== SurfaceFormat::R8G8B8X8
) {
468 SwapRAndBComponents(surf
);
472 surf
= YInvertImageSurface(surf
, map
.GetStride());
475 return surf
.forget();
478 #define CLEANUP_IF_GLERROR_OCCURRED(x) \
479 if (DidGLErrorOccur(x)) { \
483 already_AddRefed
<DataSourceSurface
> GLReadTexImageHelper::ReadTexImage(
484 GLuint aTextureId
, GLenum aTextureTarget
, const gfx::IntSize
& aSize
,
485 /* ShaderConfigOGL.mFeature */ int aConfig
, bool aYInvert
) {
486 /* Allocate resulting image surface */
487 int32_t stride
= aSize
.width
* BytesPerPixel(SurfaceFormat::R8G8B8A8
);
488 RefPtr
<DataSourceSurface
> isurf
= Factory::CreateDataSourceSurfaceWithStride(
489 aSize
, SurfaceFormat::R8G8B8A8
, stride
);
490 if (NS_WARN_IF(!isurf
)) {
494 if (!ReadTexImage(isurf
, aTextureId
, aTextureTarget
, aSize
, aConfig
,
499 return isurf
.forget();
502 bool GLReadTexImageHelper::ReadTexImage(
503 DataSourceSurface
* aDest
, GLuint aTextureId
, GLenum aTextureTarget
,
504 const gfx::IntSize
& aSize
,
505 /* ShaderConfigOGL.mFeature */ int aConfig
, bool aYInvert
) {
506 MOZ_ASSERT(aTextureTarget
== LOCAL_GL_TEXTURE_2D
||
507 aTextureTarget
== LOCAL_GL_TEXTURE_EXTERNAL
||
508 aTextureTarget
== LOCAL_GL_TEXTURE_RECTANGLE_ARB
);
512 GLint oldrb
, oldfb
, oldprog
, oldTexUnit
, oldTex
;
516 mGL
->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING
, &oldrb
);
517 mGL
->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING
, &oldfb
);
518 mGL
->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM
, &oldprog
);
519 mGL
->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE
, &oldTexUnit
);
520 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
521 switch (aTextureTarget
) {
522 case LOCAL_GL_TEXTURE_2D
:
523 mGL
->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D
, &oldTex
);
525 case LOCAL_GL_TEXTURE_EXTERNAL
:
526 mGL
->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL
, &oldTex
);
528 case LOCAL_GL_TEXTURE_RECTANGLE
:
529 mGL
->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE
, &oldTex
);
531 default: /* Already checked above */
535 ScopedGLState
scopedScissorTestState(mGL
, LOCAL_GL_SCISSOR_TEST
, false);
536 ScopedGLState
scopedBlendState(mGL
, LOCAL_GL_BLEND
, false);
537 ScopedViewportRect
scopedViewportRect(mGL
, 0, 0, aSize
.width
, aSize
.height
);
539 /* Setup renderbuffer */
540 mGL
->fGenRenderbuffers(1, &rb
);
541 mGL
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, rb
);
543 GLenum rbInternalFormat
=
544 mGL
->IsGLES() ? (mGL
->IsExtensionSupported(GLContext::OES_rgb8_rgba8
)
548 mGL
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, rbInternalFormat
,
549 aSize
.width
, aSize
.height
);
550 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer");
552 /* Setup framebuffer */
553 mGL
->fGenFramebuffers(1, &fb
);
554 mGL
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, fb
);
555 mGL
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
,
556 LOCAL_GL_COLOR_ATTACHMENT0
,
557 LOCAL_GL_RENDERBUFFER
, rb
);
558 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer");
560 MOZ_ASSERT(mGL
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
) ==
561 LOCAL_GL_FRAMEBUFFER_COMPLETE
);
563 /* Setup vertex and fragment shader */
564 GLuint program
= TextureImageProgramFor(aTextureTarget
, aConfig
);
567 mGL
->fUseProgram(program
);
568 CLEANUP_IF_GLERROR_OCCURRED("when using program");
569 mGL
->fUniform1i(mGL
->fGetUniformLocation(program
, "uTexture"), 0);
570 CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location");
572 /* Setup quad geometry */
573 mGL
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, 0);
575 float w
= (aTextureTarget
== LOCAL_GL_TEXTURE_RECTANGLE
)
578 float h
= (aTextureTarget
== LOCAL_GL_TEXTURE_RECTANGLE
)
579 ? (float)aSize
.height
582 const float vertexArray
[4 * 2] = {-1.0f
, -1.0f
, 1.0f
, -1.0f
,
583 -1.0f
, 1.0f
, 1.0f
, 1.0f
};
584 ScopedVertexAttribPointer
autoAttrib0(mGL
, 0, 2, LOCAL_GL_FLOAT
,
585 LOCAL_GL_FALSE
, 0, 0, vertexArray
);
587 const float u0
= 0.0f
;
589 const float v0
= aYInvert
? h
: 0.0f
;
590 const float v1
= aYInvert
? 0.0f
: h
;
591 const float texCoordArray
[8] = {u0
, v0
, u1
, v0
, u0
, v1
, u1
, v1
};
592 ScopedVertexAttribPointer
autoAttrib1(mGL
, 1, 2, LOCAL_GL_FLOAT
,
593 LOCAL_GL_FALSE
, 0, 0, texCoordArray
);
595 /* Bind the texture */
597 mGL
->fBindTexture(aTextureTarget
, aTextureId
);
598 CLEANUP_IF_GLERROR_OCCURRED("when binding texture");
601 mGL
->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP
, 0, 4);
602 CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
604 /* Read-back draw results */
605 ReadPixelsIntoDataSurface(mGL
, aDest
);
606 CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
609 /* Restore GL state */
610 mGL
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, oldrb
);
611 mGL
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, oldfb
);
612 mGL
->fUseProgram(oldprog
);
614 // note that deleting 0 has no effect in any of these calls
615 mGL
->fDeleteRenderbuffers(1, &rb
);
616 mGL
->fDeleteFramebuffers(1, &fb
);
618 if (aTextureId
) mGL
->fBindTexture(aTextureTarget
, oldTex
);
620 if (oldTexUnit
!= LOCAL_GL_TEXTURE0
) mGL
->fActiveTexture(oldTexUnit
);
625 #undef CLEANUP_IF_GLERROR_OCCURRED
628 } // namespace mozilla