|
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 #include "GLContext.h" |
|
9 #include "OGLShaderProgram.h" |
|
10 #include "gfxTypes.h" |
|
11 #include "gfxContext.h" |
|
12 #include "gfxImageSurface.h" |
|
13 #include "ScopedGLHelpers.h" |
|
14 #include "mozilla/gfx/2D.h" |
|
15 #include "gfx2DGlue.h" |
|
16 |
|
17 using namespace mozilla::gfx; |
|
18 |
|
19 namespace mozilla { |
|
20 namespace gl { |
|
21 |
|
22 GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl) |
|
23 : mGL(gl) |
|
24 { |
|
25 mPrograms[0] = 0; |
|
26 mPrograms[1] = 0; |
|
27 mPrograms[2] = 0; |
|
28 mPrograms[3] = 0; |
|
29 } |
|
30 |
|
31 GLReadTexImageHelper::~GLReadTexImageHelper() |
|
32 { |
|
33 mGL->fDeleteProgram(mPrograms[0]); |
|
34 mGL->fDeleteProgram(mPrograms[1]); |
|
35 mGL->fDeleteProgram(mPrograms[2]); |
|
36 mGL->fDeleteProgram(mPrograms[3]); |
|
37 } |
|
38 |
|
39 static const GLchar |
|
40 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; }"; |
|
45 |
|
46 static const GLchar |
|
47 readTextureImageFS_TEXTURE_2D[] = |
|
48 "#ifdef GL_ES\n" |
|
49 "precision mediump float;\n" |
|
50 "#endif\n" |
|
51 "varying vec2 vTexCoord;\n" |
|
52 "uniform sampler2D uTexture;\n" |
|
53 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }"; |
|
54 |
|
55 |
|
56 static const GLchar |
|
57 readTextureImageFS_TEXTURE_2D_BGRA[] = |
|
58 "#ifdef GL_ES\n" |
|
59 "precision mediump float;\n" |
|
60 "#endif\n" |
|
61 "varying vec2 vTexCoord;\n" |
|
62 "uniform sampler2D uTexture;\n" |
|
63 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }"; |
|
64 |
|
65 static const GLchar |
|
66 readTextureImageFS_TEXTURE_EXTERNAL[] = |
|
67 "#extension GL_OES_EGL_image_external : require\n" |
|
68 "#ifdef GL_ES\n" |
|
69 "precision mediump float;\n" |
|
70 "#endif\n" |
|
71 "varying vec2 vTexCoord;\n" |
|
72 "uniform samplerExternalOES uTexture;\n" |
|
73 "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }"; |
|
74 |
|
75 static const GLchar |
|
76 readTextureImageFS_TEXTURE_RECTANGLE[] = |
|
77 "#extension GL_ARB_texture_rectangle\n" |
|
78 "#ifdef GL_ES\n" |
|
79 "precision mediump float;\n" |
|
80 "#endif\n" |
|
81 "varying vec2 vTexCoord;\n" |
|
82 "uniform sampler2DRect uTexture;\n" |
|
83 "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }"; |
|
84 |
|
85 GLuint |
|
86 GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget, int aConfig) { |
|
87 int variant = 0; |
|
88 const GLchar* readTextureImageFS = nullptr; |
|
89 if (aTextureTarget == LOCAL_GL_TEXTURE_2D) |
|
90 { |
|
91 if (aConfig & mozilla::layers::ENABLE_TEXTURE_RB_SWAP) |
|
92 { // Need to swizzle R/B. |
|
93 readTextureImageFS = readTextureImageFS_TEXTURE_2D_BGRA; |
|
94 variant = 1; |
|
95 } |
|
96 else |
|
97 { |
|
98 readTextureImageFS = readTextureImageFS_TEXTURE_2D; |
|
99 variant = 0; |
|
100 } |
|
101 } else if (aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) { |
|
102 readTextureImageFS = readTextureImageFS_TEXTURE_EXTERNAL; |
|
103 variant = 2; |
|
104 } else if (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) { |
|
105 readTextureImageFS = readTextureImageFS_TEXTURE_RECTANGLE; |
|
106 variant = 3; |
|
107 } |
|
108 |
|
109 /* This might be overkill, but assure that we don't access out-of-bounds */ |
|
110 MOZ_ASSERT((size_t) variant < ArrayLength(mPrograms)); |
|
111 if (!mPrograms[variant]) { |
|
112 GLuint vs = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER); |
|
113 const GLchar* vsSourcePtr = &readTextureImageVS[0]; |
|
114 mGL->fShaderSource(vs, 1, &vsSourcePtr, nullptr); |
|
115 mGL->fCompileShader(vs); |
|
116 |
|
117 GLuint fs = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER); |
|
118 mGL->fShaderSource(fs, 1, &readTextureImageFS, nullptr); |
|
119 mGL->fCompileShader(fs); |
|
120 |
|
121 GLuint program = mGL->fCreateProgram(); |
|
122 mGL->fAttachShader(program, vs); |
|
123 mGL->fAttachShader(program, fs); |
|
124 mGL->fBindAttribLocation(program, 0, "aVertex"); |
|
125 mGL->fBindAttribLocation(program, 1, "aTexCoord"); |
|
126 mGL->fLinkProgram(program); |
|
127 |
|
128 GLint success; |
|
129 mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &success); |
|
130 |
|
131 if (!success) { |
|
132 mGL->fDeleteProgram(program); |
|
133 program = 0; |
|
134 } |
|
135 |
|
136 mGL->fDeleteShader(vs); |
|
137 mGL->fDeleteShader(fs); |
|
138 |
|
139 mPrograms[variant] = program; |
|
140 } |
|
141 |
|
142 return mPrograms[variant]; |
|
143 } |
|
144 |
|
145 bool |
|
146 GLReadTexImageHelper::DidGLErrorOccur(const char* str) |
|
147 { |
|
148 GLenum error = mGL->fGetError(); |
|
149 if (error != LOCAL_GL_NO_ERROR) { |
|
150 printf_stderr("GL ERROR: %s (0x%04x) %s\n", |
|
151 mGL->GLErrorToString(error), error, str); |
|
152 return true; |
|
153 } |
|
154 |
|
155 return false; |
|
156 } |
|
157 |
|
158 static bool |
|
159 GetActualReadFormats(GLContext* gl, |
|
160 GLenum destFormat, GLenum destType, |
|
161 GLenum& readFormat, GLenum& readType) |
|
162 { |
|
163 if (destFormat == LOCAL_GL_RGBA && |
|
164 destType == LOCAL_GL_UNSIGNED_BYTE) |
|
165 { |
|
166 readFormat = destFormat; |
|
167 readType = destType; |
|
168 return true; |
|
169 } |
|
170 |
|
171 bool fallback = true; |
|
172 if (gl->IsGLES()) { |
|
173 GLenum auxFormat = 0; |
|
174 GLenum auxType = 0; |
|
175 |
|
176 gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*)&auxFormat); |
|
177 gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*)&auxType); |
|
178 |
|
179 if (destFormat == auxFormat && |
|
180 destType == auxType) |
|
181 { |
|
182 fallback = false; |
|
183 } |
|
184 } else { |
|
185 switch (destFormat) { |
|
186 case LOCAL_GL_RGB: { |
|
187 if (destType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV) |
|
188 fallback = false; |
|
189 break; |
|
190 } |
|
191 case LOCAL_GL_BGRA: { |
|
192 if (destType == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) |
|
193 fallback = false; |
|
194 break; |
|
195 } |
|
196 } |
|
197 } |
|
198 |
|
199 if (fallback) { |
|
200 readFormat = LOCAL_GL_RGBA; |
|
201 readType = LOCAL_GL_UNSIGNED_BYTE; |
|
202 return false; |
|
203 } else { |
|
204 readFormat = destFormat; |
|
205 readType = destType; |
|
206 return true; |
|
207 } |
|
208 } |
|
209 |
|
210 static void SwapRAndBComponents(DataSourceSurface* surf) |
|
211 { |
|
212 uint8_t *row = surf->GetData(); |
|
213 if (!row) { |
|
214 MOZ_ASSERT(false, "SwapRAndBComponents: Failed to get data from DataSourceSurface."); |
|
215 return; |
|
216 } |
|
217 |
|
218 size_t rowBytes = surf->GetSize().width*4; |
|
219 size_t rowHole = surf->Stride() - rowBytes; |
|
220 |
|
221 size_t rows = surf->GetSize().height; |
|
222 |
|
223 while (rows) { |
|
224 |
|
225 const uint8_t *rowEnd = row + rowBytes; |
|
226 |
|
227 while (row != rowEnd) { |
|
228 row[0] ^= row[2]; |
|
229 row[2] ^= row[0]; |
|
230 row[0] ^= row[2]; |
|
231 row += 4; |
|
232 } |
|
233 |
|
234 row += rowHole; |
|
235 --rows; |
|
236 } |
|
237 } |
|
238 |
|
239 static uint16_t PackRGB565(uint8_t r, uint8_t g, uint8_t b) |
|
240 { |
|
241 uint16_t pixel = ((r << 11) & 0xf800) | |
|
242 ((g << 5) & 0x07e0) | |
|
243 ((b ) & 0x001f); |
|
244 |
|
245 return pixel; |
|
246 } |
|
247 |
|
248 static void CopyDataSourceSurface(DataSourceSurface* aSource, |
|
249 DataSourceSurface* aDest) |
|
250 { |
|
251 MOZ_ASSERT(aSource->GetSize() == aDest->GetSize()); |
|
252 MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 || |
|
253 aSource->GetFormat() == SurfaceFormat::R8G8B8X8); |
|
254 |
|
255 uint8_t *srcRow = aSource->GetData(); |
|
256 size_t srcRowBytes = aSource->GetSize().width * BytesPerPixel(aSource->GetFormat()); |
|
257 size_t srcRowHole = aSource->Stride() - srcRowBytes; |
|
258 |
|
259 uint8_t *destRow = aDest->GetData(); |
|
260 size_t destRowBytes = aDest->GetSize().width * BytesPerPixel(aDest->GetFormat()); |
|
261 size_t destRowHole = aDest->Stride() - destRowBytes; |
|
262 |
|
263 bool needsRBSwap = false; |
|
264 if (aDest->GetFormat() == SurfaceFormat::B8G8R8A8 || |
|
265 aDest->GetFormat() == SurfaceFormat::B8G8R8X8 || |
|
266 aDest->GetFormat() == SurfaceFormat::R5G6B5) { |
|
267 needsRBSwap = true; |
|
268 } |
|
269 |
|
270 bool needsConvertTo16Bits = false; |
|
271 if (aDest->GetFormat() == SurfaceFormat::R5G6B5) { |
|
272 needsConvertTo16Bits = true; |
|
273 } |
|
274 |
|
275 size_t rows = aSource->GetSize().height; |
|
276 |
|
277 while (rows) { |
|
278 const uint8_t *srcRowEnd = srcRow + srcRowBytes; |
|
279 |
|
280 while (srcRow != srcRowEnd) { |
|
281 uint8_t r = needsRBSwap ? srcRow[2] : srcRow[0]; |
|
282 uint8_t g = srcRow[1]; |
|
283 uint8_t b = needsRBSwap ? srcRow[0] : srcRow[2]; |
|
284 uint8_t a = srcRow[3]; |
|
285 |
|
286 if (needsConvertTo16Bits) { |
|
287 *(uint16_t*)destRow = PackRGB565(r, g, b); |
|
288 } else { |
|
289 destRow[0] = r; |
|
290 destRow[1] = g; |
|
291 destRow[2] = b; |
|
292 destRow[3] = a; |
|
293 } |
|
294 srcRow += BytesPerPixel(aSource->GetFormat()); |
|
295 destRow += BytesPerPixel(aDest->GetFormat()); |
|
296 } |
|
297 |
|
298 srcRow += srcRowHole; |
|
299 destRow += destRowHole; |
|
300 --rows; |
|
301 } |
|
302 } |
|
303 |
|
304 static int |
|
305 CalcStride(int width, int pixelSize, int alignment) |
|
306 { |
|
307 MOZ_ASSERT(alignment); |
|
308 |
|
309 int stride = width * pixelSize; |
|
310 if (stride % alignment) { // Extra at the end of the line? |
|
311 int alignmentCount = stride / alignment; |
|
312 stride = (alignmentCount+1) * alignment; |
|
313 } |
|
314 return stride; |
|
315 } |
|
316 |
|
317 static int |
|
318 GuessAlignment(int width, int pixelSize, int stride) |
|
319 { |
|
320 int alignment = 8; // Max GLES allows. |
|
321 while (CalcStride(width, pixelSize, alignment) != stride) { |
|
322 alignment /= 2; |
|
323 if (!alignment) { |
|
324 MOZ_ASSERT(alignment); |
|
325 return 1; |
|
326 } |
|
327 } |
|
328 return alignment; |
|
329 } |
|
330 |
|
331 void |
|
332 ReadPixelsIntoImageSurface(GLContext* gl, gfxImageSurface* dest) { |
|
333 gl->MakeCurrent(); |
|
334 MOZ_ASSERT(dest->GetSize() != gfxIntSize(0, 0)); |
|
335 |
|
336 /* gfxImageFormat::ARGB32: |
|
337 * RGBA+UByte: be[RGBA], le[ABGR] |
|
338 * RGBA+UInt: be[ABGR], le[RGBA] |
|
339 * BGRA+UInt: be[ARGB], le[BGRA] |
|
340 * BGRA+UIntRev: be[BGRA], le[ARGB] |
|
341 * |
|
342 * gfxImageFormat::RGB16_565: |
|
343 * RGB+UShort: le[rrrrrggg,gggbbbbb] |
|
344 */ |
|
345 bool hasAlpha = dest->Format() == gfxImageFormat::ARGB32; |
|
346 |
|
347 int destPixelSize; |
|
348 GLenum destFormat; |
|
349 GLenum destType; |
|
350 |
|
351 switch (dest->Format()) { |
|
352 case gfxImageFormat::RGB24: // XRGB |
|
353 case gfxImageFormat::ARGB32: |
|
354 destPixelSize = 4; |
|
355 // Needs host (little) endian ARGB. |
|
356 destFormat = LOCAL_GL_BGRA; |
|
357 destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; |
|
358 break; |
|
359 |
|
360 case gfxImageFormat::RGB16_565: |
|
361 destPixelSize = 2; |
|
362 destFormat = LOCAL_GL_RGB; |
|
363 destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV; |
|
364 break; |
|
365 |
|
366 default: |
|
367 MOZ_CRASH("Bad format."); |
|
368 } |
|
369 MOZ_ASSERT(dest->Width() * destPixelSize <= dest->Stride()); |
|
370 |
|
371 GLenum readFormat = destFormat; |
|
372 GLenum readType = destType; |
|
373 bool needsTempSurf = !GetActualReadFormats(gl, |
|
374 destFormat, destType, |
|
375 readFormat, readType); |
|
376 |
|
377 nsAutoPtr<gfxImageSurface> tempSurf; |
|
378 gfxImageSurface* readSurf = nullptr; |
|
379 int readAlignment = 0; |
|
380 if (needsTempSurf) { |
|
381 if (gl->DebugMode()) { |
|
382 NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); |
|
383 } |
|
384 SurfaceFormat readFormatGFX; |
|
385 |
|
386 switch (readFormat) { |
|
387 case LOCAL_GL_RGBA: |
|
388 case LOCAL_GL_BGRA: { |
|
389 readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8 |
|
390 : SurfaceFormat::B8G8R8X8; |
|
391 break; |
|
392 } |
|
393 case LOCAL_GL_RGB: { |
|
394 MOZ_ASSERT(destPixelSize == 2); |
|
395 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV); |
|
396 readFormatGFX = SurfaceFormat::R5G6B5; |
|
397 break; |
|
398 } |
|
399 default: { |
|
400 MOZ_CRASH("Bad read format."); |
|
401 } |
|
402 } |
|
403 |
|
404 switch (readType) { |
|
405 case LOCAL_GL_UNSIGNED_BYTE: { |
|
406 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); |
|
407 readAlignment = 1; |
|
408 break; |
|
409 } |
|
410 case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: { |
|
411 MOZ_ASSERT(readFormat == LOCAL_GL_BGRA); |
|
412 readAlignment = 4; |
|
413 break; |
|
414 } |
|
415 case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: { |
|
416 MOZ_ASSERT(readFormat == LOCAL_GL_RGB); |
|
417 readAlignment = 2; |
|
418 break; |
|
419 } |
|
420 default: { |
|
421 MOZ_CRASH("Bad read type."); |
|
422 } |
|
423 } |
|
424 |
|
425 tempSurf = new gfxImageSurface(dest->GetSize(), |
|
426 SurfaceFormatToImageFormat(readFormatGFX), |
|
427 false); |
|
428 readSurf = tempSurf; |
|
429 } else { |
|
430 // Figure out alignment. We don't need to know why, we just need it |
|
431 // to be valid. |
|
432 readAlignment = GuessAlignment(dest->Width(), |
|
433 destPixelSize, |
|
434 dest->Stride()); |
|
435 readSurf = dest; |
|
436 } |
|
437 MOZ_ASSERT(readAlignment); |
|
438 |
|
439 GLint currentPackAlignment = 0; |
|
440 gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, ¤tPackAlignment); |
|
441 |
|
442 if (currentPackAlignment != readAlignment) |
|
443 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment); |
|
444 |
|
445 GLsizei width = dest->Width(); |
|
446 GLsizei height = dest->Height(); |
|
447 |
|
448 readSurf->Flush(); |
|
449 gl->fReadPixels(0, 0, |
|
450 width, height, |
|
451 readFormat, readType, |
|
452 readSurf->Data()); |
|
453 readSurf->MarkDirty(); |
|
454 |
|
455 if (currentPackAlignment != readAlignment) |
|
456 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); |
|
457 |
|
458 if (readSurf != dest) { |
|
459 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); |
|
460 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); |
|
461 // So we just copied in RGBA in big endian, or le: 0xAABBGGRR. |
|
462 // We want 0xAARRGGBB, so swap R and B: |
|
463 dest->Flush(); |
|
464 RefPtr<DataSourceSurface> readDSurf = |
|
465 Factory::CreateWrappingDataSourceSurface(readSurf->Data(), |
|
466 readSurf->Stride(), |
|
467 ToIntSize(readSurf->GetSize()), |
|
468 ImageFormatToSurfaceFormat(readSurf->Format())); |
|
469 SwapRAndBComponents(readDSurf); |
|
470 dest->MarkDirty(); |
|
471 |
|
472 gfxContext ctx(dest); |
|
473 ctx.SetOperator(gfxContext::OPERATOR_SOURCE); |
|
474 ctx.SetSource(readSurf); |
|
475 ctx.Paint(); |
|
476 } |
|
477 |
|
478 // Check if GL is giving back 1.0 alpha for |
|
479 // RGBA reads to RGBA images from no-alpha buffers. |
|
480 #ifdef XP_MACOSX |
|
481 if (gl->WorkAroundDriverBugs() && |
|
482 gl->Vendor() == gl::GLVendor::NVIDIA && |
|
483 dest->Format() == gfxImageFormat::ARGB32 && |
|
484 width && height) |
|
485 { |
|
486 GLint alphaBits = 0; |
|
487 gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits); |
|
488 if (!alphaBits) { |
|
489 const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0); |
|
490 |
|
491 MOZ_ASSERT(dest->Width() * destPixelSize == dest->Stride()); |
|
492 |
|
493 dest->Flush(); |
|
494 uint32_t* itr = (uint32_t*)dest->Data(); |
|
495 uint32_t testPixel = *itr; |
|
496 if ((testPixel & alphaMask) != alphaMask) { |
|
497 // We need to set the alpha channel to 1.0 manually. |
|
498 uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4. |
|
499 |
|
500 for (; itr != itrEnd; itr++) { |
|
501 *itr |= alphaMask; |
|
502 } |
|
503 } |
|
504 dest->MarkDirty(); |
|
505 } |
|
506 } |
|
507 #endif |
|
508 } |
|
509 |
|
510 void |
|
511 ReadPixelsIntoDataSourceSurface(GLContext* gl, DataSourceSurface* dest) { |
|
512 gl->MakeCurrent(); |
|
513 MOZ_ASSERT(dest->GetSize().width != 0); |
|
514 MOZ_ASSERT(dest->GetSize().height != 0); |
|
515 |
|
516 bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 || |
|
517 dest->GetFormat() == SurfaceFormat::R8G8B8A8; |
|
518 |
|
519 int destPixelSize; |
|
520 GLenum destFormat; |
|
521 GLenum destType; |
|
522 |
|
523 switch (dest->GetFormat()) { |
|
524 case SurfaceFormat::B8G8R8A8: |
|
525 case SurfaceFormat::B8G8R8X8: |
|
526 // Needs host (little) endian ARGB. |
|
527 destFormat = LOCAL_GL_BGRA; |
|
528 destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV; |
|
529 break; |
|
530 case SurfaceFormat::R8G8B8A8: |
|
531 case SurfaceFormat::R8G8B8X8: |
|
532 // Needs host (little) endian ABGR. |
|
533 destFormat = LOCAL_GL_RGBA; |
|
534 destType = LOCAL_GL_UNSIGNED_BYTE; |
|
535 break; |
|
536 case SurfaceFormat::R5G6B5: |
|
537 destFormat = LOCAL_GL_RGB; |
|
538 destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV; |
|
539 break; |
|
540 default: |
|
541 MOZ_CRASH("Bad format."); |
|
542 } |
|
543 destPixelSize = BytesPerPixel(dest->GetFormat()); |
|
544 MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride()); |
|
545 |
|
546 GLenum readFormat = destFormat; |
|
547 GLenum readType = destType; |
|
548 bool needsTempSurf = !GetActualReadFormats(gl, |
|
549 destFormat, destType, |
|
550 readFormat, readType); |
|
551 |
|
552 RefPtr<DataSourceSurface> tempSurf; |
|
553 DataSourceSurface* readSurf = nullptr; |
|
554 int readAlignment = 0; |
|
555 if (needsTempSurf) { |
|
556 if (gl->DebugMode()) { |
|
557 NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); |
|
558 } |
|
559 SurfaceFormat readFormatGFX; |
|
560 |
|
561 // If needs temp surface, readFormat is always LOCAL_GL_RGBA |
|
562 // and readType is always LOCAL_GL_UNSIGNED_BYTE |
|
563 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); |
|
564 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); |
|
565 readFormatGFX = hasAlpha ? SurfaceFormat::R8G8B8A8 |
|
566 : SurfaceFormat::R8G8B8X8; |
|
567 readAlignment = 1; |
|
568 int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX); |
|
569 tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(), |
|
570 readFormatGFX, |
|
571 stride); |
|
572 readSurf = tempSurf; |
|
573 } else { |
|
574 // Figure out alignment. We don't need to know why, we just need it |
|
575 // to be valid. |
|
576 readAlignment = GuessAlignment(dest->GetSize().width, |
|
577 destPixelSize, |
|
578 dest->Stride()); |
|
579 readSurf = dest; |
|
580 } |
|
581 MOZ_ASSERT(readAlignment); |
|
582 MOZ_ASSERT(reinterpret_cast<uintptr_t>(readSurf->GetData()) % readAlignment == 0); |
|
583 |
|
584 GLint currentPackAlignment = 0; |
|
585 gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, ¤tPackAlignment); |
|
586 |
|
587 if (currentPackAlignment != readAlignment) |
|
588 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment); |
|
589 |
|
590 GLsizei width = dest->GetSize().width; |
|
591 GLsizei height = dest->GetSize().height; |
|
592 |
|
593 gl->fReadPixels(0, 0, |
|
594 width, height, |
|
595 readFormat, readType, |
|
596 readSurf->GetData()); |
|
597 |
|
598 if (currentPackAlignment != readAlignment) |
|
599 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); |
|
600 |
|
601 if (readSurf != dest) { |
|
602 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); |
|
603 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); |
|
604 CopyDataSourceSurface(readSurf, dest); |
|
605 } |
|
606 |
|
607 // Check if GL is giving back 1.0 alpha for |
|
608 // RGBA reads to RGBA images from no-alpha buffers. |
|
609 #ifdef XP_MACOSX |
|
610 if (gl->WorkAroundDriverBugs() && |
|
611 gl->Vendor() == gl::GLVendor::NVIDIA && |
|
612 (dest->GetFormat() == SurfaceFormat::R8G8B8A8 || |
|
613 dest->GetFormat() == SurfaceFormat::B8G8R8A8) && |
|
614 width && height) |
|
615 { |
|
616 GLint alphaBits = 0; |
|
617 gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits); |
|
618 if (!alphaBits) { |
|
619 const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0); |
|
620 |
|
621 MOZ_ASSERT(dest->GetSize().width * destPixelSize == dest->Stride()); |
|
622 |
|
623 uint32_t* itr = (uint32_t*)dest->GetData(); |
|
624 uint32_t testPixel = *itr; |
|
625 if ((testPixel & alphaMask) != alphaMask) { |
|
626 // We need to set the alpha channel to 1.0 manually. |
|
627 uint32_t* itrEnd = itr + width*height; // Stride is guaranteed to be width*4. |
|
628 |
|
629 for (; itr != itrEnd; itr++) { |
|
630 *itr |= alphaMask; |
|
631 } |
|
632 } |
|
633 } |
|
634 } |
|
635 #endif |
|
636 } |
|
637 |
|
638 static TemporaryRef<DataSourceSurface> YInvertImageSurface(DataSourceSurface* aSurf) |
|
639 { |
|
640 RefPtr<DataSourceSurface> temp = |
|
641 Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(), |
|
642 aSurf->GetFormat(), |
|
643 aSurf->Stride()); |
|
644 RefPtr<DrawTarget> dt = |
|
645 Factory::CreateDrawTargetForData(BackendType::CAIRO, |
|
646 temp->GetData(), |
|
647 temp->GetSize(), |
|
648 temp->Stride(), |
|
649 temp->GetFormat()); |
|
650 nsRefPtr<gfxContext> ctx = new gfxContext(dt); |
|
651 ctx->SetOperator(gfxContext::OPERATOR_SOURCE); |
|
652 ctx->Scale(1.0, -1.0); |
|
653 ctx->Translate(-gfxPoint(0.0, aSurf->GetSize().height)); |
|
654 |
|
655 nsRefPtr<gfxImageSurface> thebesSurf = |
|
656 new gfxImageSurface(aSurf->GetData(), |
|
657 ThebesIntSize(aSurf->GetSize()), |
|
658 aSurf->Stride(), |
|
659 SurfaceFormatToImageFormat(aSurf->GetFormat())); |
|
660 ctx->SetSource(thebesSurf); |
|
661 ctx->Paint(); |
|
662 return temp.forget(); |
|
663 } |
|
664 |
|
665 TemporaryRef<DataSourceSurface> |
|
666 ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFormat) |
|
667 { |
|
668 gl->MakeCurrent(); |
|
669 gl->GuaranteeResolve(); |
|
670 gl->fActiveTexture(LOCAL_GL_TEXTURE0); |
|
671 gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture); |
|
672 |
|
673 IntSize size; |
|
674 gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_WIDTH, &size.width); |
|
675 gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_HEIGHT, &size.height); |
|
676 |
|
677 RefPtr<DataSourceSurface> surf = |
|
678 Factory::CreateDataSourceSurfaceWithStride(size, SurfaceFormat::B8G8R8A8, |
|
679 GetAlignedStride<4>(size.width * BytesPerPixel(SurfaceFormat::B8G8R8A8))); |
|
680 |
|
681 if (!surf) { |
|
682 return nullptr; |
|
683 } |
|
684 |
|
685 uint32_t currentPackAlignment = 0; |
|
686 gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*)¤tPackAlignment); |
|
687 if (currentPackAlignment != 4) { |
|
688 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4); |
|
689 } |
|
690 gl->fGetTexImage(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, surf->GetData()); |
|
691 if (currentPackAlignment != 4) { |
|
692 gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); |
|
693 } |
|
694 |
|
695 if (aFormat == SurfaceFormat::R8G8B8A8 || aFormat == SurfaceFormat::R8G8B8X8) { |
|
696 SwapRAndBComponents(surf); |
|
697 } |
|
698 |
|
699 if (aYInvert) { |
|
700 surf = YInvertImageSurface(surf); |
|
701 } |
|
702 |
|
703 return surf.forget(); |
|
704 } |
|
705 |
|
706 void |
|
707 ReadScreenIntoImageSurface(GLContext* gl, gfxImageSurface* dest) |
|
708 { |
|
709 ScopedBindFramebuffer autoFB(gl, 0); |
|
710 ReadPixelsIntoImageSurface(gl, dest); |
|
711 } |
|
712 |
|
713 |
|
714 #define CLEANUP_IF_GLERROR_OCCURRED(x) \ |
|
715 if (DidGLErrorOccur(x)) { \ |
|
716 isurf = nullptr; \ |
|
717 break; \ |
|
718 } |
|
719 |
|
720 TemporaryRef<DataSourceSurface> |
|
721 GLReadTexImageHelper::ReadTexImage(GLuint aTextureId, |
|
722 GLenum aTextureTarget, |
|
723 const gfx::IntSize& aSize, |
|
724 /* ShaderConfigOGL.mFeature */ int aConfig, |
|
725 bool aYInvert) |
|
726 { |
|
727 MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D || |
|
728 aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL || |
|
729 aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB); |
|
730 |
|
731 mGL->MakeCurrent(); |
|
732 |
|
733 /* Allocate resulting image surface */ |
|
734 int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8); |
|
735 RefPtr<DataSourceSurface> isurf = |
|
736 Factory::CreateDataSourceSurfaceWithStride(aSize, |
|
737 SurfaceFormat::R8G8B8A8, |
|
738 stride); |
|
739 |
|
740 GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex; |
|
741 GLuint rb, fb; |
|
742 |
|
743 do { |
|
744 mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb); |
|
745 mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb); |
|
746 mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog); |
|
747 mGL->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit); |
|
748 mGL->fActiveTexture(LOCAL_GL_TEXTURE0); |
|
749 switch (aTextureTarget) { |
|
750 case LOCAL_GL_TEXTURE_2D: |
|
751 mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex); |
|
752 break; |
|
753 case LOCAL_GL_TEXTURE_EXTERNAL: |
|
754 mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldTex); |
|
755 break; |
|
756 case LOCAL_GL_TEXTURE_RECTANGLE: |
|
757 mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &oldTex); |
|
758 break; |
|
759 default: /* Already checked above */ |
|
760 break; |
|
761 } |
|
762 |
|
763 ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, false); |
|
764 ScopedGLState scopedBlendState(mGL, LOCAL_GL_BLEND, false); |
|
765 ScopedViewportRect scopedViewportRect(mGL, 0, 0, aSize.width, aSize.height); |
|
766 |
|
767 /* Setup renderbuffer */ |
|
768 mGL->fGenRenderbuffers(1, &rb); |
|
769 mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb); |
|
770 |
|
771 GLenum rbInternalFormat = |
|
772 mGL->IsGLES() |
|
773 ? (mGL->IsExtensionSupported(GLContext::OES_rgb8_rgba8) ? LOCAL_GL_RGBA8 : LOCAL_GL_RGBA4) |
|
774 : LOCAL_GL_RGBA; |
|
775 mGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, rbInternalFormat, aSize.width, aSize.height); |
|
776 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer"); |
|
777 |
|
778 /* Setup framebuffer */ |
|
779 mGL->fGenFramebuffers(1, &fb); |
|
780 mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb); |
|
781 mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, |
|
782 LOCAL_GL_RENDERBUFFER, rb); |
|
783 CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer"); |
|
784 |
|
785 MOZ_ASSERT(mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) == LOCAL_GL_FRAMEBUFFER_COMPLETE); |
|
786 |
|
787 /* Setup vertex and fragment shader */ |
|
788 GLuint program = TextureImageProgramFor(aTextureTarget, aConfig); |
|
789 MOZ_ASSERT(program); |
|
790 |
|
791 mGL->fUseProgram(program); |
|
792 CLEANUP_IF_GLERROR_OCCURRED("when using program"); |
|
793 mGL->fUniform1i(mGL->fGetUniformLocation(program, "uTexture"), 0); |
|
794 CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location"); |
|
795 |
|
796 /* Setup quad geometry */ |
|
797 mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); |
|
798 mGL->fEnableVertexAttribArray(0); |
|
799 mGL->fEnableVertexAttribArray(1); |
|
800 |
|
801 float w = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.width : 1.0f; |
|
802 float h = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.height : 1.0f; |
|
803 |
|
804 |
|
805 const float |
|
806 vertexArray[4*2] = { |
|
807 -1.0f, -1.0f, |
|
808 1.0f, -1.0f, |
|
809 -1.0f, 1.0f, |
|
810 1.0f, 1.0f |
|
811 }; |
|
812 mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, vertexArray); |
|
813 |
|
814 const float u0 = 0.0f; |
|
815 const float u1 = w; |
|
816 const float v0 = aYInvert ? h : 0.0f; |
|
817 const float v1 = aYInvert ? 0.0f : h; |
|
818 const float texCoordArray[8] = { u0, v0, |
|
819 u1, v0, |
|
820 u0, v1, |
|
821 u1, v1 }; |
|
822 mGL->fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, texCoordArray); |
|
823 |
|
824 /* Bind the texture */ |
|
825 if (aTextureId) { |
|
826 mGL->fBindTexture(aTextureTarget, aTextureId); |
|
827 CLEANUP_IF_GLERROR_OCCURRED("when binding texture"); |
|
828 } |
|
829 |
|
830 /* Draw quad */ |
|
831 mGL->fClearColor(1.0f, 0.0f, 1.0f, 1.0f); |
|
832 mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT); |
|
833 CLEANUP_IF_GLERROR_OCCURRED("when clearing color buffer"); |
|
834 |
|
835 mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); |
|
836 CLEANUP_IF_GLERROR_OCCURRED("when drawing texture"); |
|
837 |
|
838 mGL->fDisableVertexAttribArray(1); |
|
839 mGL->fDisableVertexAttribArray(0); |
|
840 |
|
841 /* Read-back draw results */ |
|
842 ReadPixelsIntoDataSourceSurface(mGL, isurf); |
|
843 CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface"); |
|
844 } while (false); |
|
845 |
|
846 /* Restore GL state */ |
|
847 //cleanup: |
|
848 mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb); |
|
849 mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb); |
|
850 mGL->fUseProgram(oldprog); |
|
851 |
|
852 // note that deleting 0 has no effect in any of these calls |
|
853 mGL->fDeleteRenderbuffers(1, &rb); |
|
854 mGL->fDeleteFramebuffers(1, &fb); |
|
855 |
|
856 if (aTextureId) |
|
857 mGL->fBindTexture(aTextureTarget, oldTex); |
|
858 |
|
859 if (oldTexUnit != LOCAL_GL_TEXTURE0) |
|
860 mGL->fActiveTexture(oldTexUnit); |
|
861 |
|
862 return isurf.forget(); |
|
863 } |
|
864 |
|
865 #undef CLEANUP_IF_GLERROR_OCCURRED |
|
866 |
|
867 |
|
868 } |
|
869 } |