• 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 <GLES2/gl2.h>
20 #include <GLES2/gl2ext.h>
21 #include <GrBackendSurface.h>
22 #include <SkCanvas.h>
23 #include <SkSurface.h>
24 #include <gl/GrGLInterface.h>
25 #include <gl/GrGLTypes.h>
26 #include "DeviceInfo.h"
27 #include "Matrix.h"
28 #include "Properties.h"
29 
30 using namespace android::uirenderer::renderthread;
31 
32 namespace android {
33 namespace uirenderer {
34 namespace skiapipeline {
35 
copyImageInto(EGLImageKHR eglImage,const Matrix4 & imgTransform,int imgWidth,int imgHeight,const Rect & srcRect,SkBitmap * bitmap)36 CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
37                                              int imgWidth, int imgHeight, const Rect& srcRect,
38                                              SkBitmap* bitmap) {
39     GLuint sourceTexId;
40     glGenTextures(1, &sourceTexId);
41     glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
42     glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
43 
44     sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
45     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
46         sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
47         LOG_ALWAYS_FATAL_IF(!glInterface.get());
48         grContext = GrContext::MakeGL(std::move(glInterface));
49     } else {
50         grContext->resetContext();
51     }
52 
53     GrGLTextureInfo externalTexture;
54     externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
55     externalTexture.fID = sourceTexId;
56 
57     GrPixelConfig pixelConfig;
58     switch (bitmap->colorType()) {
59         case kRGBA_F16_SkColorType:
60             pixelConfig = kRGBA_half_GrPixelConfig;
61             break;
62         case kN32_SkColorType:
63         default:
64             pixelConfig = kRGBA_8888_GrPixelConfig;
65             break;
66     }
67 
68     if (pixelConfig == kRGBA_half_GrPixelConfig &&
69             !grContext->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false)) {
70         ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
71         return CopyResult::DestinationInvalid;
72     }
73 
74     GrBackendTexture backendTexture(imgWidth, imgHeight, pixelConfig, externalTexture);
75 
76     CopyResult copyResult = CopyResult::UnknownError;
77     sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture,
78                                                          kTopLeft_GrSurfaceOrigin));
79     if (image) {
80         int displayedWidth = imgWidth, displayedHeight = imgHeight;
81         // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
82         // size.
83         if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
84             std::swap(displayedWidth, displayedHeight);
85         }
86         SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
87         SkRect skiaSrcRect = srcRect.toSkRect();
88         if (skiaSrcRect.isEmpty()) {
89             skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
90         }
91         bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
92 
93         if (srcNotEmpty) {
94             SkMatrix textureMatrixInv;
95             imgTransform.copyTo(textureMatrixInv);
96             // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
97             // use bottom left origin and remove flipV and invert transformations.
98             SkMatrix flipV;
99             flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
100             textureMatrixInv.preConcat(flipV);
101             textureMatrixInv.preScale(1.0f / displayedWidth, 1.0f / displayedHeight);
102             textureMatrixInv.postScale(imgWidth, imgHeight);
103             SkMatrix textureMatrix;
104             if (!textureMatrixInv.invert(&textureMatrix)) {
105                 textureMatrix = textureMatrixInv;
106             }
107 
108             textureMatrixInv.mapRect(&skiaSrcRect);
109             textureMatrixInv.mapRect(&skiaDestRect);
110 
111             // we render in an offscreen buffer to scale and to avoid an issue b/62262733
112             // with reading incorrect data from EGLImage backed SkImage (likely a driver bug)
113             sk_sp<SkSurface> scaledSurface =
114                     SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kYes, bitmap->info());
115             SkPaint paint;
116             paint.setBlendMode(SkBlendMode::kSrc);
117             // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
118             // is codified by tests using golden images like DecodeAccuracyTest.
119             if (skiaSrcRect.width() != bitmap->width() ||
120                 skiaSrcRect.height() != bitmap->height()) {
121                 // TODO: apply filter always, but check if tests will be fine
122                 paint.setFilterQuality(kLow_SkFilterQuality);
123             }
124             scaledSurface->getCanvas()->concat(textureMatrix);
125             scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint,
126                                                       SkCanvas::kFast_SrcRectConstraint);
127 
128             image = scaledSurface->makeImageSnapshot();
129 
130             if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
131                 bitmap->notifyPixelsChanged();
132                 copyResult = CopyResult::Success;
133             }
134         }
135     }
136 
137     // make sure that we have deleted the texture (in the SkImage) before we
138     // destroy the EGLImage that it was created from
139     image.reset();
140     return copyResult;
141 }
142 
143 } /* namespace skiapipeline */
144 } /* namespace uirenderer */
145 } /* namespace android */
146