Coverage Report

Created: 2018-09-25 14:53

/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*)&currentPackAlignment);
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