summaryrefslogtreecommitdiff
path: root/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
blob: a18d26471a297ac9b132cf89bd0b2c8468814aaa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "SkiaOpenGLReadback.h"

#include "Matrix.h"
#include "Properties.h"
#include <SkCanvas.h>
#include <SkSurface.h>
#include <gl/GrGLInterface.h>
#include <gl/GrGLTypes.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

using namespace android::uirenderer::renderthread;

namespace android {
namespace uirenderer {
namespace skiapipeline {

CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
        int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) {

    GLuint sourceTexId;
    glGenTextures(1, &sourceTexId);
    glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);

    sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
        sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
        LOG_ALWAYS_FATAL_IF(!glInterface.get());
        grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend,
                (GrBackendContext)glInterface.get()));
    } else {
        grContext->resetContext();
    }

    GrGLTextureInfo externalTexture;
    externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
    externalTexture.fID = sourceTexId;

    GrBackendTextureDesc textureDescription;
    textureDescription.fWidth = imgWidth;
    textureDescription.fHeight = imgHeight;
    textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
    textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
    textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);

    CopyResult copyResult = CopyResult::UnknownError;
    sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), textureDescription));
    if (image) {
        SkAutoLockPixels alp(*bitmap);

        // convert to Skia data structures
        const SkRect bufferRect = SkRect::MakeIWH(imgWidth, imgHeight);
        SkRect skiaSrcRect = srcRect.toSkRect();
        SkMatrix textureMatrix;
        imgTransform.copyTo(textureMatrix);

        // remove the y-flip applied to the matrix so that we can scale the srcRect.
        // This flip is not needed as we specify the origin of the texture when we
        // wrap it as an SkImage.
        SkMatrix yFlip = SkMatrix::MakeScale(1, -1);
        yFlip.postTranslate(0,1);
        textureMatrix.preConcat(yFlip);

        // copy the entire src if the rect is empty
        if (skiaSrcRect.isEmpty()) {
            skiaSrcRect = bufferRect;
        }

        // since the y-flip has been removed we can simply scale & translate
        // the source rectangle
        textureMatrix.mapRect(&skiaSrcRect);

        if (skiaSrcRect.intersect(bufferRect)) {
            SkPoint srcOrigin = SkPoint::Make(skiaSrcRect.fLeft, skiaSrcRect.fTop);

            // if we need to scale the result we must render to an offscreen buffer
            if (bitmap->width() != skiaSrcRect.width()
                    || bitmap->height() != skiaSrcRect.height()) {
                sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
                        grContext.get(), SkBudgeted::kYes, bitmap->info());
                SkPaint paint;
                paint.setBlendMode(SkBlendMode::kSrc);
                scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect,
                        SkRect::MakeWH(bitmap->width(), bitmap->height()), &paint);
                image = scaledSurface->makeImageSnapshot();
                srcOrigin.set(0,0);
            }

            if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
                                  srcOrigin.fX, srcOrigin.fY)) {
                copyResult = CopyResult::Success;
            }
        }
    }

    // make sure that we have deleted the texture (in the SkImage) before we
    // destroy the EGLImage that it was created from
    image.reset();
    return copyResult;
}

} /* namespace skiapipeline */
} /* namespace uirenderer */
} /* namespace android */