• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "SkiaOpenGLReadback.h"
18 
19 #include "Matrix.h"
20 #include "Properties.h"
21 #include <SkCanvas.h>
22 #include <SkSurface.h>
23 #include <GrBackendSurface.h>
24 #include <gl/GrGLInterface.h>
25 #include <gl/GrGLTypes.h>
26 #include <GLES2/gl2.h>
27 #include <GLES2/gl2ext.h>
28 
29 using namespace android::uirenderer::renderthread;
30 
31 namespace android {
32 namespace uirenderer {
33 namespace skiapipeline {
34 
copyImageInto(EGLImageKHR eglImage,const Matrix4 & imgTransform,int imgWidth,int imgHeight,const Rect & srcRect,SkBitmap * bitmap)35 CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
36         int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) {
37 
38     GLuint sourceTexId;
39     glGenTextures(1, &sourceTexId);
40     glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
41     glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
42 
43     sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
44     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
45         sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
46         LOG_ALWAYS_FATAL_IF(!glInterface.get());
47         grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend,
48                 (GrBackendContext)glInterface.get()));
49     } else {
50         grContext->resetContext();
51     }
52 
53     GrGLTextureInfo externalTexture;
54     externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
55     externalTexture.fID = sourceTexId;
56 
57     GrBackendTexture backendTexture(imgWidth, imgHeight, kRGBA_8888_GrPixelConfig, externalTexture);
58 
59     CopyResult copyResult = CopyResult::UnknownError;
60     sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture,
61             kTopLeft_GrSurfaceOrigin));
62     if (image) {
63         SkMatrix textureMatrix;
64         imgTransform.copyTo(textureMatrix);
65 
66         // remove the y-flip applied to the matrix
67         SkMatrix yFlip = SkMatrix::MakeScale(1, -1);
68         yFlip.postTranslate(0,1);
69         textureMatrix.preConcat(yFlip);
70 
71         // multiply by image size, because textureMatrix maps to [0..1] range
72         textureMatrix[SkMatrix::kMTransX] *= imgWidth;
73         textureMatrix[SkMatrix::kMTransY] *= imgHeight;
74 
75         // swap rotation and translation part of the matrix, because we convert from
76         // right-handed Cartesian to left-handed coordinate system.
77         std::swap(textureMatrix[SkMatrix::kMTransX], textureMatrix[SkMatrix::kMTransY]);
78         std::swap(textureMatrix[SkMatrix::kMSkewX], textureMatrix[SkMatrix::kMSkewY]);
79 
80         // convert to Skia data structures
81         SkRect skiaSrcRect = srcRect.toSkRect();
82         SkMatrix textureMatrixInv;
83         SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
84         bool srcNotEmpty = false;
85         if (textureMatrix.invert(&textureMatrixInv)) {
86             if (skiaSrcRect.isEmpty()) {
87                 skiaSrcRect = SkRect::MakeIWH(imgWidth, imgHeight);
88                 srcNotEmpty = !skiaSrcRect.isEmpty();
89             } else {
90                 // src and dest rectangles need to be converted into texture coordinates before the
91                 // rotation matrix is applied (because drawImageRect preconcat its matrix).
92                 textureMatrixInv.mapRect(&skiaSrcRect);
93                 srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(imgWidth, imgHeight));
94             }
95             textureMatrixInv.mapRect(&skiaDestRect);
96         }
97 
98         if (srcNotEmpty) {
99             // we render in an offscreen buffer to scale and to avoid an issue b/62262733
100             // with reading incorrect data from EGLImage backed SkImage (likely a driver bug)
101             sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
102                     grContext.get(), SkBudgeted::kYes, bitmap->info());
103             SkPaint paint;
104             paint.setBlendMode(SkBlendMode::kSrc);
105             scaledSurface->getCanvas()->concat(textureMatrix);
106             scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint);
107 
108             image = scaledSurface->makeImageSnapshot();
109 
110             if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
111                 bitmap->notifyPixelsChanged();
112                 copyResult = CopyResult::Success;
113             }
114         }
115     }
116 
117     // make sure that we have deleted the texture (in the SkImage) before we
118     // destroy the EGLImage that it was created from
119     image.reset();
120     return copyResult;
121 }
122 
123 } /* namespace skiapipeline */
124 } /* namespace uirenderer */
125 } /* namespace android */
126