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"
22 using namespace mozilla::gfx
;
24 GLReadTexImageHelper::GLReadTexImageHelper(GLContext
* gl
) : mGL(gl
) {
31 GLReadTexImageHelper::~GLReadTexImageHelper() {
32 if (!mGL
->MakeCurrent()) return;
34 mGL
->fDeleteProgram(mPrograms
[0]);
35 mGL
->fDeleteProgram(mPrograms
[1]);
36 mGL
->fDeleteProgram(mPrograms
[2]);
37 mGL
->fDeleteProgram(mPrograms
[3]);
40 static const GLchar readTextureImageVS
[] =
41 "attribute vec2 aVertex;\n"
42 "attribute vec2 aTexCoord;\n"
43 "varying vec2 vTexCoord;\n"
44 "void main() { gl_Position = vec4(aVertex, 0, 1); vTexCoord = aTexCoord; }";
46 static const GLchar readTextureImageFS_TEXTURE_2D
[] =
48 "precision mediump float;\n"
50 "varying vec2 vTexCoord;\n"
51 "uniform sampler2D uTexture;\n"
52 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
54 static const GLchar readTextureImageFS_TEXTURE_2D_BGRA
[] =
56 "precision mediump float;\n"
58 "varying vec2 vTexCoord;\n"
59 "uniform sampler2D uTexture;\n"
60 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }";
62 static const GLchar readTextureImageFS_TEXTURE_EXTERNAL
[] =
63 "#extension GL_OES_EGL_image_external : require\n"
65 "precision mediump float;\n"
67 "varying vec2 vTexCoord;\n"
68 "uniform samplerExternalOES uTexture;\n"
69 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }";
71 static const GLchar readTextureImageFS_TEXTURE_RECTANGLE
[] =
72 "#extension GL_ARB_texture_rectangle\n"
74 "precision mediump float;\n"
76 "varying vec2 vTexCoord;\n"
77 "uniform sampler2DRect uTexture;\n"
78 "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }";
80 GLuint
GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget
,
83 const GLchar
* readTextureImageFS
= nullptr;
84 if (aTextureTarget
== LOCAL_GL_TEXTURE_2D
) {
85 if (aConfig
& mozilla::layers::ENABLE_TEXTURE_RB_SWAP
) {
86 // Need to swizzle R/B.
87 readTextureImageFS
= readTextureImageFS_TEXTURE_2D_BGRA
;
90 readTextureImageFS
= readTextureImageFS_TEXTURE_2D
;
93 } else if (aTextureTarget
== LOCAL_GL_TEXTURE_EXTERNAL
) {
94 readTextureImageFS
= readTextureImageFS_TEXTURE_EXTERNAL
;
96 } else if (aTextureTarget
== LOCAL_GL_TEXTURE_RECTANGLE
) {
97 readTextureImageFS
= readTextureImageFS_TEXTURE_RECTANGLE
;
101 /* This might be overkill, but assure that we don't access out-of-bounds */
102 MOZ_ASSERT((size_t)variant
< ArrayLength(mPrograms
));
103 if (!mPrograms
[variant
]) {
104 GLuint vs
= mGL
->fCreateShader(LOCAL_GL_VERTEX_SHADER
);
105 const GLchar
* vsSourcePtr
= &readTextureImageVS
[0];
106 mGL
->fShaderSource(vs
, 1, &vsSourcePtr
, nullptr);
107 mGL
->fCompileShader(vs
);
109 GLuint fs
= mGL
->fCreateShader(LOCAL_GL_FRAGMENT_SHADER
);
110 mGL
->fShaderSource(fs
, 1, &readTextureImageFS
, nullptr);
111 mGL
->fCompileShader(fs
);
113 GLuint program
= mGL
->fCreateProgram();
114 mGL
->fAttachShader(program
, vs
);
115 mGL
->fAttachShader(program
, fs
);
116 mGL
->fBindAttribLocation(program
, 0, "aVertex");
117 mGL
->fBindAttribLocation(program
, 1, "aTexCoord");
118 mGL
->fLinkProgram(program
);
121 mGL
->fGetProgramiv(program
, LOCAL_GL_LINK_STATUS
, &success
);
124 mGL
->fDeleteProgram(program
);
128 mGL
->fDeleteShader(vs
);
129 mGL
->fDeleteShader(fs
);
131 mPrograms
[variant
] = program
;
134 return mPrograms
[variant
];
137 bool GLReadTexImageHelper::DidGLErrorOccur(const char* str
) {
138 GLenum error
= mGL
->fGetError();
139 if (error
!= LOCAL_GL_NO_ERROR
) {
140 printf_stderr("GL ERROR: %s %s\n",
141 GLContext::GLErrorToString(error
).c_str(), str
);
148 bool GetActualReadFormats(GLContext
* gl
, GLenum destFormat
, GLenum destType
,
149 GLenum
* out_readFormat
, GLenum
* out_readType
) {
150 MOZ_ASSERT(out_readFormat
);
151 MOZ_ASSERT(out_readType
);
153 if (destFormat
== LOCAL_GL_RGBA
&& destType
== LOCAL_GL_UNSIGNED_BYTE
) {
154 *out_readFormat
= destFormat
;
155 *out_readType
= destType
;
159 bool fallback
= true;
161 GLenum auxFormat
= 0;
164 gl
->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT
,
166 gl
->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE
, (GLint
*)&auxType
);
168 if (destFormat
== auxFormat
&& destType
== auxType
) {
172 switch (destFormat
) {
174 if (destType
== LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV
) fallback
= false;
177 case LOCAL_GL_BGRA
: {
178 if (destType
== LOCAL_GL_UNSIGNED_BYTE
||
179 destType
== LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
) {
188 *out_readFormat
= LOCAL_GL_RGBA
;
189 *out_readType
= LOCAL_GL_UNSIGNED_BYTE
;
192 *out_readFormat
= destFormat
;
193 *out_readType
= destType
;
198 void SwapRAndBComponents(DataSourceSurface
* surf
) {
199 DataSourceSurface::MappedSurface map
;
200 if (!surf
->Map(DataSourceSurface::MapType::READ_WRITE
, &map
)) {
201 MOZ_ASSERT(false, "SwapRAndBComponents: Failed to map surface.");
204 MOZ_ASSERT(map
.mStride
>= 0);
206 const size_t rowBytes
= surf
->GetSize().width
* 4;
207 const size_t rowHole
= map
.mStride
- rowBytes
;
209 uint8_t* row
= map
.mData
;
212 "SwapRAndBComponents: Failed to get data from"
213 " DataSourceSurface.");
218 const size_t rows
= surf
->GetSize().height
;
219 for (size_t i
= 0; i
< rows
; i
++) {
220 const uint8_t* rowEnd
= row
+ rowBytes
;
222 while (row
!= rowEnd
) {
223 std::swap(row
[0], row
[2]);
233 static int CalcRowStride(int width
, int pixelSize
, int alignment
) {
234 MOZ_ASSERT(alignment
);
236 int rowStride
= width
* pixelSize
;
237 if (rowStride
% alignment
) { // Extra at the end of the line?
238 int alignmentCount
= rowStride
/ alignment
;
239 rowStride
= (alignmentCount
+ 1) * alignment
;
244 static int GuessAlignment(int width
, int pixelSize
, int rowStride
) {
245 int alignment
= 8; // Max GLES allows.
246 while (CalcRowStride(width
, pixelSize
, alignment
) != rowStride
) {
249 NS_WARNING("Bad alignment for GLES. Will use temp surf for readback.");
256 void ReadPixelsIntoDataSurface(GLContext
* gl
, DataSourceSurface
* dest
) {
258 MOZ_ASSERT(dest
->GetSize().width
!= 0);
259 MOZ_ASSERT(dest
->GetSize().height
!= 0);
261 bool hasAlpha
= dest
->GetFormat() == SurfaceFormat::B8G8R8A8
||
262 dest
->GetFormat() == SurfaceFormat::R8G8B8A8
;
268 switch (dest
->GetFormat()) {
269 case SurfaceFormat::B8G8R8A8
:
270 case SurfaceFormat::B8G8R8X8
:
271 // Needs host (little) endian ARGB.
272 destFormat
= LOCAL_GL_BGRA
;
273 destType
= LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
;
275 case SurfaceFormat::R8G8B8A8
:
276 case SurfaceFormat::R8G8B8X8
:
277 // Needs host (little) endian ABGR.
278 destFormat
= LOCAL_GL_RGBA
;
279 destType
= LOCAL_GL_UNSIGNED_BYTE
;
281 case SurfaceFormat::R5G6B5_UINT16
:
282 destFormat
= LOCAL_GL_RGB
;
283 destType
= LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV
;
286 MOZ_CRASH("GFX: Bad format, read pixels.");
288 destPixelSize
= BytesPerPixel(dest
->GetFormat());
290 Maybe
<DataSourceSurface::ScopedMap
> map
;
291 map
.emplace(dest
, DataSourceSurface::READ_WRITE
);
293 MOZ_ASSERT(dest
->GetSize().width
* destPixelSize
<= map
->GetStride());
295 GLenum readFormat
= destFormat
;
296 GLenum readType
= destType
;
298 !GetActualReadFormats(gl
, destFormat
, destType
, &readFormat
, &readType
);
300 RefPtr
<DataSourceSurface
> tempSurf
;
301 DataSourceSurface
* readSurf
= dest
;
303 GuessAlignment(dest
->GetSize().width
, destPixelSize
, map
->GetStride());
304 if (!readAlignment
) {
305 needsTempSurf
= true;
308 if (GLContext::ShouldSpew()) {
310 "Needing intermediary surface for ReadPixels. This will be slow!");
312 SurfaceFormat readFormatGFX
;
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
= dest
->GetSize().width
* BytesPerPixel(readFormatGFX
);
358 tempSurf
= Factory::CreateDataSourceSurfaceWithStride(
359 dest
->GetSize(), readFormatGFX
, stride
);
360 if (NS_WARN_IF(!tempSurf
)) {
366 map
.emplace(readSurf
, DataSourceSurface::READ_WRITE
);
369 MOZ_ASSERT(readAlignment
);
370 MOZ_ASSERT(reinterpret_cast<uintptr_t>(map
->GetData()) % readAlignment
== 0);
372 GLsizei width
= dest
->GetSize().width
;
373 GLsizei height
= dest
->GetSize().height
;
376 ScopedPackState
safePackState(gl
);
377 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, readAlignment
);
379 gl
->fReadPixels(0, 0, width
, height
, readFormat
, readType
, map
->GetData());
384 if (readSurf
!= dest
) {
385 gfx::Factory::CopyDataSourceSurface(readSurf
, dest
);
389 already_AddRefed
<gfx::DataSourceSurface
> YInvertImageSurface(
390 gfx::DataSourceSurface
* aSurf
, uint32_t aStride
) {
391 RefPtr
<DataSourceSurface
> temp
= Factory::CreateDataSourceSurfaceWithStride(
392 aSurf
->GetSize(), aSurf
->GetFormat(), aStride
);
393 if (NS_WARN_IF(!temp
)) {
397 DataSourceSurface::MappedSurface map
;
398 if (!temp
->Map(DataSourceSurface::MapType::WRITE
, &map
)) {
402 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
403 BackendType::CAIRO
, map
.mData
, temp
->GetSize(), map
.mStride
,
410 dt
->SetTransform(Matrix::Scaling(1.0, -1.0) *
411 Matrix::Translation(0.0, aSurf
->GetSize().height
));
412 Rect
rect(0, 0, aSurf
->GetSize().width
, aSurf
->GetSize().height
);
414 aSurf
, rect
, rect
, DrawSurfaceOptions(),
415 DrawOptions(1.0, CompositionOp::OP_SOURCE
, AntialiasMode::NONE
));
417 return temp
.forget();
420 already_AddRefed
<DataSourceSurface
> ReadBackSurface(GLContext
* gl
,
423 SurfaceFormat aFormat
) {
425 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
);
426 gl
->fBindTexture(LOCAL_GL_TEXTURE_2D
, aTexture
);
429 gl
->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D
, 0, LOCAL_GL_TEXTURE_WIDTH
,
431 gl
->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D
, 0, LOCAL_GL_TEXTURE_HEIGHT
,
434 RefPtr
<DataSourceSurface
> surf
= Factory::CreateDataSourceSurfaceWithStride(
435 size
, SurfaceFormat::B8G8R8A8
,
436 GetAlignedStride
<4>(size
.width
, BytesPerPixel(SurfaceFormat::B8G8R8A8
)));
438 if (NS_WARN_IF(!surf
)) {
442 uint32_t currentPackAlignment
= 0;
443 gl
->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT
, (GLint
*)¤tPackAlignment
);
444 if (currentPackAlignment
!= 4) {
445 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, 4);
448 DataSourceSurface::ScopedMap
map(surf
, DataSourceSurface::READ
);
449 gl
->fGetTexImage(LOCAL_GL_TEXTURE_2D
, 0, LOCAL_GL_RGBA
,
450 LOCAL_GL_UNSIGNED_BYTE
, map
.GetData());
452 if (currentPackAlignment
!= 4) {
453 gl
->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT
, currentPackAlignment
);
456 if (aFormat
== SurfaceFormat::R8G8B8A8
||
457 aFormat
== SurfaceFormat::R8G8B8X8
) {
458 SwapRAndBComponents(surf
);
462 surf
= YInvertImageSurface(surf
, map
.GetStride());
465 return surf
.forget();
468 #define CLEANUP_IF_GLERROR_OCCURRED(x) \
469 if (DidGLErrorOccur(x)) { \
473 already_AddRefed
<DataSourceSurface
> GLReadTexImageHelper::ReadTexImage(
474 GLuint aTextureId
, GLenum aTextureTarget
, const gfx::IntSize
& aSize
,
475 /* ShaderConfigOGL.mFeature */ int aConfig
, bool aYInvert
) {
476 /* Allocate resulting image surface */
477 int32_t stride
= aSize
.width
* BytesPerPixel(SurfaceFormat::R8G8B8A8
);
478 RefPtr
<DataSourceSurface
> isurf
= Factory::CreateDataSourceSurfaceWithStride(
479 aSize
, SurfaceFormat::R8G8B8A8
, stride
);
480 if (NS_WARN_IF(!isurf
)) {
484 if (!ReadTexImage(isurf
, aTextureId
, aTextureTarget
, aSize
, aConfig
,
489 return isurf
.forget();
492 bool GLReadTexImageHelper::ReadTexImage(
493 DataSourceSurface
* aDest
, GLuint aTextureId
, GLenum aTextureTarget
,
494 const gfx::IntSize
& aSize
,
495 /* ShaderConfigOGL.mFeature */ int aConfig
, bool aYInvert
) {
496 MOZ_ASSERT(aTextureTarget
== LOCAL_GL_TEXTURE_2D
||
497 aTextureTarget
== LOCAL_GL_TEXTURE_EXTERNAL
||
498 aTextureTarget
== LOCAL_GL_TEXTURE_RECTANGLE_ARB
);
502 GLint oldrb
, oldfb
, oldprog
, oldTexUnit
, oldTex
;
506 mGL
->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING
, &oldrb
);
507 mGL
->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING
, &oldfb
);
508 mGL
->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM
, &oldprog
);
509 mGL
->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE
, &oldTexUnit
);
510 mGL
->fActiveTexture(LOCAL_GL_TEXTURE0
);
511 switch (aTextureTarget
) {
512 case LOCAL_GL_TEXTURE_2D
:
513 mGL
->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D
, &oldTex
);
515 case LOCAL_GL_TEXTURE_EXTERNAL
:
516 mGL
->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL
, &oldTex
);
518 case LOCAL_GL_TEXTURE_RECTANGLE
:
519 mGL
->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE
, &oldTex
);
521 default: /* Already checked above */
525 ScopedGLState
scopedScissorTestState(mGL
, LOCAL_GL_SCISSOR_TEST
, false);
526 ScopedGLState
scopedBlendState(mGL
, LOCAL_GL_BLEND
, false);
527 ScopedViewportRect
scopedViewportRect(mGL
, 0, 0, aSize
.width
, aSize
.height
);
529 /* Setup renderbuffer */
530 mGL
->fGenRenderbuffers(1, &rb
);
531 mGL
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, rb
);
533 GLenum rbInternalFormat
=
534 mGL
->IsGLES() ? (mGL
->IsExtensionSupported(GLContext::OES_rgb8_rgba8
)
538 mGL
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, rbInternalFormat
,
539 aSize
.width
, aSize
.height
);
540 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer");
542 /* Setup framebuffer */
543 mGL
->fGenFramebuffers(1, &fb
);
544 mGL
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, fb
);
545 mGL
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
,
546 LOCAL_GL_COLOR_ATTACHMENT0
,
547 LOCAL_GL_RENDERBUFFER
, rb
);
548 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer");
550 MOZ_ASSERT(mGL
->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
) ==
551 LOCAL_GL_FRAMEBUFFER_COMPLETE
);
553 /* Setup vertex and fragment shader */
554 GLuint program
= TextureImageProgramFor(aTextureTarget
, aConfig
);
557 mGL
->fUseProgram(program
);
558 CLEANUP_IF_GLERROR_OCCURRED("when using program");
559 mGL
->fUniform1i(mGL
->fGetUniformLocation(program
, "uTexture"), 0);
560 CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location");
562 /* Setup quad geometry */
563 mGL
->fBindBuffer(LOCAL_GL_ARRAY_BUFFER
, 0);
565 float w
= (aTextureTarget
== LOCAL_GL_TEXTURE_RECTANGLE
)
568 float h
= (aTextureTarget
== LOCAL_GL_TEXTURE_RECTANGLE
)
569 ? (float)aSize
.height
572 const float vertexArray
[4 * 2] = {-1.0f
, -1.0f
, 1.0f
, -1.0f
,
573 -1.0f
, 1.0f
, 1.0f
, 1.0f
};
574 ScopedVertexAttribPointer
autoAttrib0(mGL
, 0, 2, LOCAL_GL_FLOAT
,
575 LOCAL_GL_FALSE
, 0, 0, vertexArray
);
577 const float u0
= 0.0f
;
579 const float v0
= aYInvert
? h
: 0.0f
;
580 const float v1
= aYInvert
? 0.0f
: h
;
581 const float texCoordArray
[8] = {u0
, v0
, u1
, v0
, u0
, v1
, u1
, v1
};
582 ScopedVertexAttribPointer
autoAttrib1(mGL
, 1, 2, LOCAL_GL_FLOAT
,
583 LOCAL_GL_FALSE
, 0, 0, texCoordArray
);
585 /* Bind the texture */
587 mGL
->fBindTexture(aTextureTarget
, aTextureId
);
588 CLEANUP_IF_GLERROR_OCCURRED("when binding texture");
591 mGL
->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP
, 0, 4);
592 CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
594 /* Read-back draw results */
595 ReadPixelsIntoDataSurface(mGL
, aDest
);
596 CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
599 /* Restore GL state */
600 mGL
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, oldrb
);
601 mGL
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, oldfb
);
602 mGL
->fUseProgram(oldprog
);
604 // note that deleting 0 has no effect in any of these calls
605 mGL
->fDeleteRenderbuffers(1, &rb
);
606 mGL
->fDeleteFramebuffers(1, &fb
);
608 if (aTextureId
) mGL
->fBindTexture(aTextureTarget
, oldTex
);
610 if (oldTexUnit
!= LOCAL_GL_TEXTURE0
) mGL
->fActiveTexture(oldTexUnit
);
615 #undef CLEANUP_IF_GLERROR_OCCURRED
618 } // namespace mozilla