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