void
ReadPixelsIntoImageSurface(GLContext* gl, gfxImageSurface* dest) {
    gl->MakeCurrent();
    MOZ_ASSERT(dest->GetSize() != gfxIntSize(0, 0));

    /* gfxImageFormat::ARGB32:
     * RGBA+UByte:   be[RGBA], le[ABGR]
     * RGBA+UInt:    be[ABGR], le[RGBA]
     * BGRA+UInt:    be[ARGB], le[BGRA]
     * BGRA+UIntRev: be[BGRA], le[ARGB]
     *
     * gfxImageFormat::RGB16_565:
     * RGB+UShort: le[rrrrrggg,gggbbbbb]
     */
    bool hasAlpha = dest->Format() == gfxImageFormat::ARGB32;

    int destPixelSize;
    GLenum destFormat;
    GLenum destType;

    switch (dest->Format()) {
        case gfxImageFormat::RGB24: // XRGB
        case gfxImageFormat::ARGB32:
            destPixelSize = 4;
            // Needs host (little) endian ARGB.
            destFormat = LOCAL_GL_BGRA;
            destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
            break;

        case gfxImageFormat::RGB16_565:
            destPixelSize = 2;
            destFormat = LOCAL_GL_RGB;
            destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
            break;

        default:
            MOZ_CRASH("Bad format.");
    }
    MOZ_ASSERT(dest->Width() * destPixelSize <= dest->Stride());

    GLenum readFormat = destFormat;
    GLenum readType = destType;
    bool needsTempSurf = !GetActualReadFormats(gl,
                                               destFormat, destType,
                                               readFormat, readType);

    nsAutoPtr<gfxImageSurface> tempSurf;
    gfxImageSurface* readSurf = nullptr;
    int readAlignment = 0;
    if (needsTempSurf) {
        if (gl->DebugMode()) {
            NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!");
        }
        SurfaceFormat readFormatGFX;

        switch (readFormat) {
            case LOCAL_GL_RGBA:
            case LOCAL_GL_BGRA: {
                readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8
                                         : SurfaceFormat::B8G8R8X8;
                break;
            }
            case LOCAL_GL_RGB: {
                MOZ_ASSERT(destPixelSize == 2);
                MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV);
                readFormatGFX = SurfaceFormat::R5G6B5;
                break;
            }
            default: {
                MOZ_CRASH("Bad read format.");
            }
        }

        switch (readType) {
            case LOCAL_GL_UNSIGNED_BYTE: {
                MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
                readAlignment = 1;
                break;
            }
            case LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV: {
                MOZ_ASSERT(readFormat == LOCAL_GL_BGRA);
                readAlignment = 4;
                break;
            }
            case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: {
                MOZ_ASSERT(readFormat == LOCAL_GL_RGB);
                readAlignment = 2;
                break;
            }
            default: {
                MOZ_CRASH("Bad read type.");
            }
        }

        tempSurf = new gfxImageSurface(dest->GetSize(),
                                       SurfaceFormatToImageFormat(readFormatGFX),
                                       false);
        readSurf = tempSurf;
    } else {
        // Figure out alignment. We don't need to know why, we just need it
        // to be valid.
        readAlignment = GuessAlignment(dest->Width(),
                                       destPixelSize,
                                       dest->Stride());
        readSurf = dest;
    }
    MOZ_ASSERT(readAlignment);

    GLint currentPackAlignment = 0;
    gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &currentPackAlignment);

    if (currentPackAlignment != readAlignment)
        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);

    GLsizei width = dest->Width();
    GLsizei height = dest->Height();

    readSurf->Flush();
    gl->fReadPixels(0, 0,
                    width, height,
                    readFormat, readType,
                    readSurf->Data());
    readSurf->MarkDirty();

    if (currentPackAlignment != readAlignment)
        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);

    if (readSurf != dest) {
        MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
        MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
        // So we just copied in RGBA in big endian, or le: 0xAABBGGRR.
        // We want 0xAARRGGBB, so swap R and B:
        dest->Flush();
        RefPtr<DataSourceSurface> readDSurf =
            Factory::CreateWrappingDataSourceSurface(readSurf->Data(),
                                                     readSurf->Stride(),
                                                     ToIntSize(readSurf->GetSize()),
                                                     ImageFormatToSurfaceFormat(readSurf->Format()));
        SwapRAndBComponents(readDSurf);
        dest->MarkDirty();

        gfxContext ctx(dest);
        ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
        ctx.SetSource(readSurf);
        ctx.Paint();
    }

    // Check if GL is giving back 1.0 alpha for
    // RGBA reads to RGBA images from no-alpha buffers.
#ifdef XP_MACOSX
    if (gl->WorkAroundDriverBugs() &&
        gl->Vendor() == gl::GLVendor::NVIDIA &&
        dest->Format() == gfxImageFormat::ARGB32 &&
        width && height)
    {
        GLint alphaBits = 0;
        gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits);
        if (!alphaBits) {
            const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0);

            MOZ_ASSERT(dest->Width() * destPixelSize == dest->Stride());

            dest->Flush();
            uint32_t* itr = (uint32_t*)dest->Data();
            uint32_t testPixel = *itr;
            if ((testPixel & alphaMask) != alphaMask) {
                // We need to set the alpha channel to 1.0 manually.
                uint32_t* itrEnd = itr + width*height;  // Stride is guaranteed to be width*4.

                for (; itr != itrEnd; itr++) {
                    *itr |= alphaMask;
                }
            }
            dest->MarkDirty();
        }
    }
#endif
}
示例#2
0
/*static*/ bool
TexUnpackSurface::UploadDataSurface(bool isSubImage, WebGLContext* webgl,
                                    TexImageTarget target, GLint level,
                                    const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                    GLint yOffset, GLint zOffset, GLsizei width,
                                    GLsizei height, gfx::DataSourceSurface* surf,
                                    bool isSurfAlphaPremult, GLenum* const out_glError)
{
    gl::GLContext* gl = webgl->GL();
    MOZ_ASSERT(gl->IsCurrent());
    *out_glError = 0;

    if (isSurfAlphaPremult != webgl->mPixelStore_PremultiplyAlpha)
        return false;

    gl::OriginPos srcOrigin, dstOrigin;
    OriginsForDOM(webgl, &srcOrigin, &dstOrigin);
    if (srcOrigin != dstOrigin)
        return false;

    // This differs from the raw-data upload in that we choose how we do the unpack.
    // (alignment, etc.)

    // Uploading RGBX as RGBA and blitting to RGB is faster than repacking RGBX into
    // RGB on the CPU. However, this is optimization is out-of-scope for now.

    static const webgl::DriverUnpackInfo kInfoBGRA = {
        LOCAL_GL_BGRA,
        LOCAL_GL_BGRA,
        LOCAL_GL_UNSIGNED_BYTE,
    };

    const webgl::DriverUnpackInfo* chosenDUI = nullptr;

    switch (surf->GetFormat()) {
    case gfx::SurfaceFormat::B8G8R8A8:
        if (SupportsBGRA(gl) &&
            dui->internalFormat == LOCAL_GL_RGBA &&
            dui->unpackFormat == LOCAL_GL_RGBA &&
            dui->unpackType == LOCAL_GL_UNSIGNED_BYTE)
        {
            chosenDUI = &kInfoBGRA;
        }
        break;

    case gfx::SurfaceFormat::R8G8B8A8:
        if (dui->unpackFormat == LOCAL_GL_RGBA &&
            dui->unpackType == LOCAL_GL_UNSIGNED_BYTE)
        {
            chosenDUI = dui;
        }
        break;

    case gfx::SurfaceFormat::R5G6B5_UINT16:
        if (dui->unpackFormat == LOCAL_GL_RGB &&
            dui->unpackType == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
        {
            chosenDUI = dui;
        }
        break;

    default:
        break;
    }

    if (!chosenDUI)
        return false;

    gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::MapType::READ);
    if (!map.IsMapped())
        return false;

    const webgl::PackingInfo pi = {chosenDUI->unpackFormat, chosenDUI->unpackType};
    const auto bytesPerPixel = webgl::BytesPerPixel(pi);
    const size_t bytesPerRow = width * bytesPerPixel;

    const GLint kMaxUnpackAlignment = 8;
    size_t unpackAlignment;
    if (!GuessAlignment(map.GetData(), bytesPerRow, map.GetStride(), kMaxUnpackAlignment,
                        &unpackAlignment))
    {
        return false;
        // TODO: Consider using UNPACK_ settings to set the stride based on the too-large
        // alignment used for some SourceSurfaces. (D2D allegedy likes alignment=16)
    }

    MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() );
    ScopedUnpackReset scopedReset(webgl);
    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, unpackAlignment);

    const GLsizei depth = 1;
    GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, chosenDUI,
                                   xOffset, yOffset, zOffset, width, height, depth,
                                   map.GetData());
    if (error) {
        *out_glError = error;
        return false;
    }

    return true;
}