/src/mozilla-central/gfx/gl/GLReadTexImageHelper.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* vim: set ts=8 sts=4 et sw=4 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/. */ |
6 | | |
7 | | #include "GLReadTexImageHelper.h" |
8 | | |
9 | | #include "gfx2DGlue.h" |
10 | | #include "gfxColor.h" |
11 | | #include "gfxTypes.h" |
12 | | #include "GLContext.h" |
13 | | #include "OGLShaderProgram.h" |
14 | | #include "ScopedGLHelpers.h" |
15 | | |
16 | | #include "mozilla/gfx/2D.h" |
17 | | #include "mozilla/Move.h" |
18 | | |
19 | | namespace mozilla { |
20 | | namespace gl { |
21 | | |
22 | | using namespace mozilla::gfx; |
23 | | |
24 | | GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl) |
25 | | : mGL(gl) |
26 | 0 | { |
27 | 0 | mPrograms[0] = 0; |
28 | 0 | mPrograms[1] = 0; |
29 | 0 | mPrograms[2] = 0; |
30 | 0 | mPrograms[3] = 0; |
31 | 0 | } |
32 | | |
33 | | GLReadTexImageHelper::~GLReadTexImageHelper() |
34 | 0 | { |
35 | 0 | if (!mGL->MakeCurrent()) |
36 | 0 | return; |
37 | 0 | |
38 | 0 | mGL->fDeleteProgram(mPrograms[0]); |
39 | 0 | mGL->fDeleteProgram(mPrograms[1]); |
40 | 0 | mGL->fDeleteProgram(mPrograms[2]); |
41 | 0 | mGL->fDeleteProgram(mPrograms[3]); |
42 | 0 | } |
43 | | |
44 | | static const GLchar |
45 | | readTextureImageVS[] = |
46 | | "attribute vec2 aVertex;\n" |
47 | | "attribute vec2 aTexCoord;\n" |
48 | | "varying vec2 vTexCoord;\n" |
49 | | "void main() { gl_Position = vec4(aVertex, 0, 1); vTexCoord = aTexCoord; }"; |
50 | | |
51 | | static const GLchar |
52 | | readTextureImageFS_TEXTURE_2D[] = |
53 | | "#ifdef GL_ES\n" |
54 | | "precision mediump float;\n" |
55 | | "#endif\n" |
56 | | "varying vec2 vTexCoord;\n" |
57 | | "uniform sampler2D uTexture;\n" |
58 | | "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }"; |
59 | | |
60 | | |
61 | | static const GLchar |
62 | | readTextureImageFS_TEXTURE_2D_BGRA[] = |
63 | | "#ifdef GL_ES\n" |
64 | | "precision mediump float;\n" |
65 | | "#endif\n" |
66 | | "varying vec2 vTexCoord;\n" |
67 | | "uniform sampler2D uTexture;\n" |
68 | | "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }"; |
69 | | |
70 | | static const GLchar |
71 | | readTextureImageFS_TEXTURE_EXTERNAL[] = |
72 | | "#extension GL_OES_EGL_image_external : require\n" |
73 | | "#ifdef GL_ES\n" |
74 | | "precision mediump float;\n" |
75 | | "#endif\n" |
76 | | "varying vec2 vTexCoord;\n" |
77 | | "uniform samplerExternalOES uTexture;\n" |
78 | | "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }"; |
79 | | |
80 | | static const GLchar |
81 | | readTextureImageFS_TEXTURE_RECTANGLE[] = |
82 | | "#extension GL_ARB_texture_rectangle\n" |
83 | | "#ifdef GL_ES\n" |
84 | | "precision mediump float;\n" |
85 | | "#endif\n" |
86 | | "varying vec2 vTexCoord;\n" |
87 | | "uniform sampler2DRect uTexture;\n" |
88 | | "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }"; |
89 | | |
90 | | GLuint |
91 | | GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget, |
92 | | int aConfig) |
93 | 0 | { |
94 | 0 | int variant = 0; |
95 | 0 | const GLchar* readTextureImageFS = nullptr; |
96 | 0 | if (aTextureTarget == LOCAL_GL_TEXTURE_2D) { |
97 | 0 | if (aConfig & mozilla::layers::ENABLE_TEXTURE_RB_SWAP) { |
98 | 0 | // Need to swizzle R/B. |
99 | 0 | readTextureImageFS = readTextureImageFS_TEXTURE_2D_BGRA; |
100 | 0 | variant = 1; |
101 | 0 | } else { |
102 | 0 | readTextureImageFS = readTextureImageFS_TEXTURE_2D; |
103 | 0 | variant = 0; |
104 | 0 | } |
105 | 0 | } else if (aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) { |
106 | 0 | readTextureImageFS = readTextureImageFS_TEXTURE_EXTERNAL; |
107 | 0 | variant = 2; |
108 | 0 | } else if (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) { |
109 | 0 | readTextureImageFS = readTextureImageFS_TEXTURE_RECTANGLE; |
110 | 0 | variant = 3; |
111 | 0 | } |
112 | 0 |
|
113 | 0 | /* This might be overkill, but assure that we don't access out-of-bounds */ |
114 | 0 | MOZ_ASSERT((size_t) variant < ArrayLength(mPrograms)); |
115 | 0 | if (!mPrograms[variant]) { |
116 | 0 | GLuint vs = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER); |
117 | 0 | const GLchar* vsSourcePtr = &readTextureImageVS[0]; |
118 | 0 | mGL->fShaderSource(vs, 1, &vsSourcePtr, nullptr); |
119 | 0 | mGL->fCompileShader(vs); |
120 | 0 |
|
121 | 0 | GLuint fs = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER); |
122 | 0 | mGL->fShaderSource(fs, 1, &readTextureImageFS, nullptr); |
123 | 0 | mGL->fCompileShader(fs); |
124 | 0 |
|
125 | 0 | GLuint program = mGL->fCreateProgram(); |
126 | 0 | mGL->fAttachShader(program, vs); |
127 | 0 | mGL->fAttachShader(program, fs); |
128 | 0 | mGL->fBindAttribLocation(program, 0, "aVertex"); |
129 | 0 | mGL->fBindAttribLocation(program, 1, "aTexCoord"); |
130 | 0 | mGL->fLinkProgram(program); |
131 | 0 |
|
132 | 0 | GLint success; |
133 | 0 | mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &success); |
134 | 0 |
|
135 | 0 | if (!success) { |
136 | 0 | mGL->fDeleteProgram(program); |
137 | 0 | program = 0; |
138 | 0 | } |
139 | 0 |
|
140 | 0 | mGL->fDeleteShader(vs); |
141 | 0 | mGL->fDeleteShader(fs); |
142 | 0 |
|
143 | 0 | mPrograms[variant] = program; |
144 | 0 | } |
145 | 0 |
|
146 | 0 | return mPrograms[variant]; |
147 | 0 | } |
148 | | |
149 | | bool |
150 | | GLReadTexImageHelper::DidGLErrorOccur(const char* str) |
151 | 0 | { |
152 | 0 | GLenum error = mGL->fGetError(); |
153 | 0 | if (error != LOCAL_GL_NO_ERROR) { |
154 | 0 | printf_stderr("GL ERROR: %s (0x%04x) %s\n", |
155 | 0 | GLContext::GLErrorToString(error), error, str); |
156 | 0 | return true; |
157 | 0 | } |
158 | 0 | |
159 | 0 | return false; |
160 | 0 | } |
161 | | |
162 | | bool |
163 | | GetActualReadFormats(GLContext* gl, |
164 | | GLenum destFormat, GLenum destType, |
165 | | GLenum* out_readFormat, GLenum* out_readType) |
166 | 0 | { |
167 | 0 | MOZ_ASSERT(out_readFormat); |
168 | 0 | MOZ_ASSERT(out_readType); |
169 | 0 |
|
170 | 0 | if (destFormat == LOCAL_GL_RGBA && |
171 | 0 | destType == LOCAL_GL_UNSIGNED_BYTE) |
172 | 0 | { |
173 | 0 | *out_readFormat = destFormat; |
174 | 0 | *out_readType = destType; |
175 | 0 | return true; |
176 | 0 | } |
177 | 0 | |
178 | 0 | bool fallback = true; |
179 | 0 | if (gl->IsGLES()) { |
180 | 0 | GLenum auxFormat = 0; |
181 | 0 | GLenum auxType = 0; |
182 | 0 |
|
183 | 0 | gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&auxFormat); |
184 | 0 | gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&auxType); |
185 | 0 |
|
186 | 0 | if (destFormat == auxFormat && |
187 | 0 | destType == auxType) |
188 | 0 | { |
189 | 0 | fallback = false; |
190 | 0 | } |
191 | 0 | } else { |
192 | 0 | switch (destFormat) { |
193 | 0 | case LOCAL_GL_RGB: { |
194 | 0 | if (destType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV) |
195 | 0 | fallback = false; |
196 | 0 | break; |
197 | 0 | } |
198 | 0 | case LOCAL_GL_BGRA: { |
199 | 0 | if (destType == LOCAL_GL_UNSIGNED_BYTE || |
200 | 0 | destType == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) |
201 | 0 | { |
202 | 0 | fallback = false; |
203 | 0 | } |
204 | 0 | break; |
205 | 0 | } |
206 | 0 | } |
207 | 0 | } |
208 | 0 |
|
209 | 0 | if (fallback) { |
210 | 0 | *out_readFormat = LOCAL_GL_RGBA; |
211 | 0 | *out_readType = LOCAL_GL_UNSIGNED_BYTE; |
212 | 0 | return false; |
213 | 0 | } else { |
214 | 0 | *out_readFormat = destFormat; |
215 | 0 | *out_readType = destType; |
216 | 0 | return true; |
217 | 0 | } |
218 | 0 | } |
219 | | |
220 | | void |
221 | | SwapRAndBComponents(DataSourceSurface* surf) |
222 | 0 | { |
223 | 0 | DataSourceSurface::MappedSurface map; |
224 | 0 | if (!surf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) { |
225 | 0 | MOZ_ASSERT(false, "SwapRAndBComponents: Failed to map surface."); |
226 | 0 | return; |
227 | 0 | } |
228 | 0 | MOZ_ASSERT(map.mStride >= 0); |
229 | 0 |
|
230 | 0 | const size_t rowBytes = surf->GetSize().width*4; |
231 | 0 | const size_t rowHole = map.mStride - rowBytes; |
232 | 0 |
|
233 | 0 | uint8_t* row = map.mData; |
234 | 0 | if (!row) { |
235 | 0 | MOZ_ASSERT(false, "SwapRAndBComponents: Failed to get data from" |
236 | 0 | " DataSourceSurface."); |
237 | 0 | surf->Unmap(); |
238 | 0 | return; |
239 | 0 | } |
240 | 0 |
|
241 | 0 | const size_t rows = surf->GetSize().height; |
242 | 0 | for (size_t i = 0; i < rows; i++) { |
243 | 0 | const uint8_t* rowEnd = row + rowBytes; |
244 | 0 |
|
245 | 0 | while (row != rowEnd) { |
246 | 0 | Swap(row[0], row[2]); |
247 | 0 | row += 4; |
248 | 0 | } |
249 | 0 |
|
250 | 0 | row += rowHole; |
251 | 0 | } |
252 | 0 |
|
253 | 0 | surf->Unmap(); |
254 | 0 | } |
255 | | |
256 | | static int |
257 | | CalcRowStride(int width, int pixelSize, int alignment) |
258 | 0 | { |
259 | 0 | MOZ_ASSERT(alignment); |
260 | 0 |
|
261 | 0 | int rowStride = width * pixelSize; |
262 | 0 | if (rowStride % alignment) { // Extra at the end of the line? |
263 | 0 | int alignmentCount = rowStride / alignment; |
264 | 0 | rowStride = (alignmentCount+1) * alignment; |
265 | 0 | } |
266 | 0 | return rowStride; |
267 | 0 | } |
268 | | |
269 | | static int |
270 | | GuessAlignment(int width, int pixelSize, int rowStride) |
271 | 0 | { |
272 | 0 | int alignment = 8; // Max GLES allows. |
273 | 0 | while (CalcRowStride(width, pixelSize, alignment) != rowStride) { |
274 | 0 | alignment /= 2; |
275 | 0 | if (!alignment) { |
276 | 0 | NS_WARNING("Bad alignment for GLES. Will use temp surf for readback."); |
277 | 0 | return 0; |
278 | 0 | } |
279 | 0 | } |
280 | 0 | return alignment; |
281 | 0 | } |
282 | | |
283 | | void |
284 | | ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest) |
285 | 0 | { |
286 | 0 | gl->MakeCurrent(); |
287 | 0 | MOZ_ASSERT(dest->GetSize().width != 0); |
288 | 0 | MOZ_ASSERT(dest->GetSize().height != 0); |
289 | 0 |
|
290 | 0 | bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 || |
291 | 0 | dest->GetFormat() == SurfaceFormat::R8G8B8A8; |
292 | 0 |
|
293 | 0 | int destPixelSize; |
294 | 0 | GLenum destFormat; |
295 | 0 | GLenum destType; |
296 | 0 |
|
297 | 0 | switch (dest->GetFormat()) { |
298 | 0 | case SurfaceFormat::B8G8R8A8: |
299 | 0 | case SurfaceFormat::B8G8R8X8: |
300 | 0 | // Needs host (little) endian ARGB. |
301 | 0 | destFormat = LOCAL_GL_BGRA; |
302 | 0 | destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; |
303 | 0 | break; |
304 | 0 | case SurfaceFormat::R8G8B8A8: |
305 | 0 | case SurfaceFormat::R8G8B8X8: |
306 | 0 | // Needs host (little) endian ABGR. |
307 | 0 | destFormat = LOCAL_GL_RGBA; |
308 | 0 | destType = LOCAL_GL_UNSIGNED_BYTE; |
309 | 0 | break; |
310 | 0 | case SurfaceFormat::R5G6B5_UINT16: |
311 | 0 | destFormat = LOCAL_GL_RGB; |
312 | 0 | destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV; |
313 | 0 | break; |
314 | 0 | default: |
315 | 0 | MOZ_CRASH("GFX: Bad format, read pixels."); |
316 | 0 | } |
317 | 0 | destPixelSize = BytesPerPixel(dest->GetFormat()); |
318 | 0 |
|
319 | 0 | Maybe<DataSourceSurface::ScopedMap> map; |
320 | 0 | map.emplace(dest, DataSourceSurface::READ_WRITE); |
321 | 0 |
|
322 | 0 | MOZ_ASSERT(dest->GetSize().width * destPixelSize <= map->GetStride()); |
323 | 0 |
|
324 | 0 | GLenum readFormat = destFormat; |
325 | 0 | GLenum readType = destType; |
326 | 0 | bool needsTempSurf = !GetActualReadFormats(gl, |
327 | 0 | destFormat, destType, |
328 | 0 | &readFormat, &readType); |
329 | 0 |
|
330 | 0 | RefPtr<DataSourceSurface> tempSurf; |
331 | 0 | DataSourceSurface* readSurf = dest; |
332 | 0 | int readAlignment = GuessAlignment(dest->GetSize().width, |
333 | 0 | destPixelSize, |
334 | 0 | map->GetStride()); |
335 | 0 | if (!readAlignment) { |
336 | 0 | needsTempSurf = true; |
337 | 0 | } |
338 | 0 | if (needsTempSurf) { |
339 | 0 | if (GLContext::ShouldSpew()) { |
340 | 0 | NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); |
341 | 0 | } |
342 | 0 | SurfaceFormat readFormatGFX; |
343 | 0 |
|
344 | 0 | switch (readFormat) { |
345 | 0 | case LOCAL_GL_RGBA: { |
346 | 0 | readFormatGFX = hasAlpha ? SurfaceFormat::R8G8B8A8 |
347 | 0 | : SurfaceFormat::R8G8B8X8; |
348 | 0 | break; |
349 | 0 | } |
350 | 0 | case LOCAL_GL_BGRA: { |
351 | 0 | readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8 |
352 | 0 | : SurfaceFormat::B8G8R8X8; |
353 | 0 | break; |
354 | 0 | } |
355 | 0 | case LOCAL_GL_RGB: { |
356 | 0 | MOZ_ASSERT(destPixelSize == 2); |
357 | 0 | MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV); |
358 | 0 | readFormatGFX = SurfaceFormat::R5G6B5_UINT16; |
359 | 0 | break; |
360 | 0 | } |
361 | 0 | default: { |
362 | 0 | MOZ_CRASH("GFX: Bad read format, read format."); |
363 | 0 | } |
364 | 0 | } |
365 | 0 |
|
366 | 0 | switch (readType) { |
367 | 0 | case LOCAL_GL_UNSIGNED_BYTE: { |
368 | 0 | MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); |
369 | 0 | readAlignment = 1; |
370 | 0 | break; |
371 | 0 | } |
372 | 0 | case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: { |
373 | 0 | MOZ_ASSERT(readFormat == LOCAL_GL_BGRA); |
374 | 0 | readAlignment = 4; |
375 | 0 | break; |
376 | 0 | } |
377 | 0 | case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: { |
378 | 0 | MOZ_ASSERT(readFormat == LOCAL_GL_RGB); |
379 | 0 | readAlignment = 2; |
380 | 0 | break; |
381 | 0 | } |
382 | 0 | default: { |
383 | 0 | MOZ_CRASH("GFX: Bad read type, read type."); |
384 | 0 | } |
385 | 0 | } |
386 | 0 |
|
387 | 0 | int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX); |
388 | 0 | tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(), |
389 | 0 | readFormatGFX, |
390 | 0 | stride); |
391 | 0 | if (NS_WARN_IF(!tempSurf)) { |
392 | 0 | return; |
393 | 0 | } |
394 | 0 | |
395 | 0 | readSurf = tempSurf; |
396 | 0 | map = Nothing(); |
397 | 0 | map.emplace(readSurf, DataSourceSurface::READ_WRITE); |
398 | 0 | } |
399 | 0 |
|
400 | 0 | MOZ_ASSERT(readAlignment); |
401 | 0 | MOZ_ASSERT(reinterpret_cast<uintptr_t>(map->GetData()) % readAlignment == 0); |
402 | 0 |
|
403 | 0 | GLsizei width = dest->GetSize().width; |
404 | 0 | GLsizei height = dest->GetSize().height; |
405 | 0 |
|
406 | 0 | { |
407 | 0 | ScopedPackState safePackState(gl); |
408 | 0 | gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment); |
409 | 0 |
|
410 | 0 | gl->fReadPixels(0, 0, |
411 | 0 | width, height, |
412 | 0 | readFormat, readType, |
413 | 0 | map->GetData()); |
414 | 0 | } |
415 | 0 |
|
416 | 0 | map = Nothing(); |
417 | 0 |
|
418 | 0 | if (readSurf != dest) { |
419 | 0 | MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); |
420 | 0 | MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); |
421 | 0 | gfx::Factory::CopyDataSourceSurface(readSurf, dest); |
422 | 0 | } |
423 | 0 | } |
424 | | |
425 | | already_AddRefed<gfx::DataSourceSurface> |
426 | | YInvertImageSurface(gfx::DataSourceSurface* aSurf, uint32_t aStride) |
427 | 0 | { |
428 | 0 | RefPtr<DataSourceSurface> temp = |
429 | 0 | Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(), |
430 | 0 | aSurf->GetFormat(), |
431 | 0 | aStride); |
432 | 0 | if (NS_WARN_IF(!temp)) { |
433 | 0 | return nullptr; |
434 | 0 | } |
435 | 0 | |
436 | 0 | DataSourceSurface::MappedSurface map; |
437 | 0 | if (!temp->Map(DataSourceSurface::MapType::WRITE, &map)) { |
438 | 0 | return nullptr; |
439 | 0 | } |
440 | 0 | |
441 | 0 | RefPtr<DrawTarget> dt = |
442 | 0 | Factory::CreateDrawTargetForData(BackendType::CAIRO, |
443 | 0 | map.mData, |
444 | 0 | temp->GetSize(), |
445 | 0 | map.mStride, |
446 | 0 | temp->GetFormat()); |
447 | 0 | if (!dt) { |
448 | 0 | temp->Unmap(); |
449 | 0 | return nullptr; |
450 | 0 | } |
451 | 0 | |
452 | 0 | dt->SetTransform(Matrix::Scaling(1.0, -1.0) * |
453 | 0 | Matrix::Translation(0.0, aSurf->GetSize().height)); |
454 | 0 | Rect rect(0, 0, aSurf->GetSize().width, aSurf->GetSize().height); |
455 | 0 | dt->DrawSurface(aSurf, rect, rect, DrawSurfaceOptions(), |
456 | 0 | DrawOptions(1.0, CompositionOp::OP_SOURCE, AntialiasMode::NONE)); |
457 | 0 | temp->Unmap(); |
458 | 0 | return temp.forget(); |
459 | 0 | } |
460 | | |
461 | | already_AddRefed<DataSourceSurface> |
462 | | ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFormat) |
463 | 0 | { |
464 | 0 | gl->MakeCurrent(); |
465 | 0 | gl->GuaranteeResolve(); |
466 | 0 | gl->fActiveTexture(LOCAL_GL_TEXTURE0); |
467 | 0 | gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture); |
468 | 0 |
|
469 | 0 | IntSize size; |
470 | 0 | gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_WIDTH, &size.width); |
471 | 0 | gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_HEIGHT, &size.height); |
472 | 0 |
|
473 | 0 | RefPtr<DataSourceSurface> surf = |
474 | 0 | Factory::CreateDataSourceSurfaceWithStride(size, SurfaceFormat::B8G8R8A8, |
475 | 0 | GetAlignedStride<4>(size.width, BytesPerPixel(SurfaceFormat::B8G8R8A8))); |
476 | 0 |
|
477 | 0 | if (NS_WARN_IF(!surf)) { |
478 | 0 | return nullptr; |
479 | 0 | } |
480 | 0 | |
481 | 0 | uint32_t currentPackAlignment = 0; |
482 | 0 | gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*)¤tPackAlignment); |
483 | 0 | if (currentPackAlignment != 4) { |
484 | 0 | gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4); |
485 | 0 | } |
486 | 0 |
|
487 | 0 | DataSourceSurface::ScopedMap map(surf, DataSourceSurface::READ); |
488 | 0 | gl->fGetTexImage(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, map.GetData()); |
489 | 0 |
|
490 | 0 | if (currentPackAlignment != 4) { |
491 | 0 | gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); |
492 | 0 | } |
493 | 0 |
|
494 | 0 | if (aFormat == SurfaceFormat::R8G8B8A8 || aFormat == SurfaceFormat::R8G8B8X8) { |
495 | 0 | SwapRAndBComponents(surf); |
496 | 0 | } |
497 | 0 |
|
498 | 0 | if (aYInvert) { |
499 | 0 | surf = YInvertImageSurface(surf, map.GetStride()); |
500 | 0 | } |
501 | 0 |
|
502 | 0 | return surf.forget(); |
503 | 0 | } |
504 | | |
505 | | #define CLEANUP_IF_GLERROR_OCCURRED(x) \ |
506 | 0 | if (DidGLErrorOccur(x)) { \ |
507 | 0 | return false; \ |
508 | 0 | } |
509 | | |
510 | | already_AddRefed<DataSourceSurface> |
511 | | GLReadTexImageHelper::ReadTexImage(GLuint aTextureId, |
512 | | GLenum aTextureTarget, |
513 | | const gfx::IntSize& aSize, |
514 | | /* ShaderConfigOGL.mFeature */ int aConfig, |
515 | | bool aYInvert) |
516 | 0 | { |
517 | 0 | /* Allocate resulting image surface */ |
518 | 0 | int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8); |
519 | 0 | RefPtr<DataSourceSurface> isurf = |
520 | 0 | Factory::CreateDataSourceSurfaceWithStride(aSize, |
521 | 0 | SurfaceFormat::R8G8B8A8, |
522 | 0 | stride); |
523 | 0 | if (NS_WARN_IF(!isurf)) { |
524 | 0 | return nullptr; |
525 | 0 | } |
526 | 0 | |
527 | 0 | if (!ReadTexImage(isurf, aTextureId, aTextureTarget, aSize, aConfig, aYInvert)) { |
528 | 0 | return nullptr; |
529 | 0 | } |
530 | 0 | |
531 | 0 | return isurf.forget(); |
532 | 0 | } |
533 | | |
534 | | bool |
535 | | GLReadTexImageHelper::ReadTexImage(DataSourceSurface* aDest, |
536 | | GLuint aTextureId, |
537 | | GLenum aTextureTarget, |
538 | | const gfx::IntSize& aSize, |
539 | | /* ShaderConfigOGL.mFeature */ int aConfig, |
540 | | bool aYInvert) |
541 | 0 | { |
542 | 0 | MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D || |
543 | 0 | aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL || |
544 | 0 | aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB); |
545 | 0 |
|
546 | 0 | mGL->MakeCurrent(); |
547 | 0 |
|
548 | 0 | GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex; |
549 | 0 | GLuint rb, fb; |
550 | 0 |
|
551 | 0 | do { |
552 | 0 | mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb); |
553 | 0 | mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb); |
554 | 0 | mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog); |
555 | 0 | mGL->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit); |
556 | 0 | mGL->fActiveTexture(LOCAL_GL_TEXTURE0); |
557 | 0 | switch (aTextureTarget) { |
558 | 0 | case LOCAL_GL_TEXTURE_2D: |
559 | 0 | mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex); |
560 | 0 | break; |
561 | 0 | case LOCAL_GL_TEXTURE_EXTERNAL: |
562 | 0 | mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldTex); |
563 | 0 | break; |
564 | 0 | case LOCAL_GL_TEXTURE_RECTANGLE: |
565 | 0 | mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &oldTex); |
566 | 0 | break; |
567 | 0 | default: /* Already checked above */ |
568 | 0 | break; |
569 | 0 | } |
570 | 0 | |
571 | 0 | ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, false); |
572 | 0 | ScopedGLState scopedBlendState(mGL, LOCAL_GL_BLEND, false); |
573 | 0 | ScopedViewportRect scopedViewportRect(mGL, 0, 0, aSize.width, aSize.height); |
574 | 0 |
|
575 | 0 | /* Setup renderbuffer */ |
576 | 0 | mGL->fGenRenderbuffers(1, &rb); |
577 | 0 | mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb); |
578 | 0 |
|
579 | 0 | GLenum rbInternalFormat = |
580 | 0 | mGL->IsGLES() |
581 | 0 | ? (mGL->IsExtensionSupported(GLContext::OES_rgb8_rgba8) ? LOCAL_GL_RGBA8 : LOCAL_GL_RGBA4) |
582 | 0 | : LOCAL_GL_RGBA; |
583 | 0 | mGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, rbInternalFormat, aSize.width, aSize.height); |
584 | 0 | CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer"); |
585 | 0 |
|
586 | 0 | /* Setup framebuffer */ |
587 | 0 | mGL->fGenFramebuffers(1, &fb); |
588 | 0 | mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb); |
589 | 0 | mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, |
590 | 0 | LOCAL_GL_RENDERBUFFER, rb); |
591 | 0 | CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer"); |
592 | 0 |
|
593 | 0 | MOZ_ASSERT(mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) == LOCAL_GL_FRAMEBUFFER_COMPLETE); |
594 | 0 |
|
595 | 0 | /* Setup vertex and fragment shader */ |
596 | 0 | GLuint program = TextureImageProgramFor(aTextureTarget, aConfig); |
597 | 0 | MOZ_ASSERT(program); |
598 | 0 |
|
599 | 0 | mGL->fUseProgram(program); |
600 | 0 | CLEANUP_IF_GLERROR_OCCURRED("when using program"); |
601 | 0 | mGL->fUniform1i(mGL->fGetUniformLocation(program, "uTexture"), 0); |
602 | 0 | CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location"); |
603 | 0 |
|
604 | 0 | /* Setup quad geometry */ |
605 | 0 | mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); |
606 | 0 |
|
607 | 0 | float w = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.width : 1.0f; |
608 | 0 | float h = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.height : 1.0f; |
609 | 0 |
|
610 | 0 | const float |
611 | 0 | vertexArray[4*2] = { |
612 | 0 | -1.0f, -1.0f, |
613 | 0 | 1.0f, -1.0f, |
614 | 0 | -1.0f, 1.0f, |
615 | 0 | 1.0f, 1.0f |
616 | 0 | }; |
617 | 0 | ScopedVertexAttribPointer autoAttrib0(mGL, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, vertexArray); |
618 | 0 |
|
619 | 0 | const float u0 = 0.0f; |
620 | 0 | const float u1 = w; |
621 | 0 | const float v0 = aYInvert ? h : 0.0f; |
622 | 0 | const float v1 = aYInvert ? 0.0f : h; |
623 | 0 | const float texCoordArray[8] = { u0, v0, |
624 | 0 | u1, v0, |
625 | 0 | u0, v1, |
626 | 0 | u1, v1 }; |
627 | 0 | ScopedVertexAttribPointer autoAttrib1(mGL, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, texCoordArray); |
628 | 0 |
|
629 | 0 | /* Bind the texture */ |
630 | 0 | if (aTextureId) { |
631 | 0 | mGL->fBindTexture(aTextureTarget, aTextureId); |
632 | 0 | CLEANUP_IF_GLERROR_OCCURRED("when binding texture"); |
633 | 0 | } |
634 | 0 |
|
635 | 0 | mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); |
636 | 0 | CLEANUP_IF_GLERROR_OCCURRED("when drawing texture"); |
637 | 0 |
|
638 | 0 | /* Read-back draw results */ |
639 | 0 | ReadPixelsIntoDataSurface(mGL, aDest); |
640 | 0 | CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface"); |
641 | 0 | } while (false); |
642 | 0 |
|
643 | 0 | /* Restore GL state */ |
644 | 0 | mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb); |
645 | 0 | mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb); |
646 | 0 | mGL->fUseProgram(oldprog); |
647 | 0 |
|
648 | 0 | // note that deleting 0 has no effect in any of these calls |
649 | 0 | mGL->fDeleteRenderbuffers(1, &rb); |
650 | 0 | mGL->fDeleteFramebuffers(1, &fb); |
651 | 0 |
|
652 | 0 | if (aTextureId) |
653 | 0 | mGL->fBindTexture(aTextureTarget, oldTex); |
654 | 0 |
|
655 | 0 | if (oldTexUnit != LOCAL_GL_TEXTURE0) |
656 | 0 | mGL->fActiveTexture(oldTexUnit); |
657 | 0 |
|
658 | 0 | return true; |
659 | 0 | } |
660 | | |
661 | | #undef CLEANUP_IF_GLERROR_OCCURRED |
662 | | |
663 | | } // namespace gl |
664 | | } // namespace mozilla |