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 <gl/GrGLInterface.h>
24 #include <gl/GrGLTypes.h>
25 #include <GLES2/gl2.h>
26 #include <GLES2/gl2ext.h>
27
28 using namespace android::uirenderer::renderthread;
29
30 namespace android {
31 namespace uirenderer {
32 namespace skiapipeline {
33
copyImageInto(EGLImageKHR eglImage,const Matrix4 & imgTransform,int imgWidth,int imgHeight,const Rect & srcRect,SkBitmap * bitmap)34 CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
35 int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) {
36
37 GLuint sourceTexId;
38 glGenTextures(1, &sourceTexId);
39 glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
40 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
41
42 sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
43 if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
44 sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
45 LOG_ALWAYS_FATAL_IF(!glInterface.get());
46 grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend,
47 (GrBackendContext)glInterface.get()));
48 } else {
49 grContext->resetContext();
50 }
51
52 GrGLTextureInfo externalTexture;
53 externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
54 externalTexture.fID = sourceTexId;
55
56 GrBackendTextureDesc textureDescription;
57 textureDescription.fWidth = imgWidth;
58 textureDescription.fHeight = imgHeight;
59 textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
60 textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
61 textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
62
63 CopyResult copyResult = CopyResult::UnknownError;
64 sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), textureDescription));
65 if (image) {
66 SkAutoLockPixels alp(*bitmap);
67
68 // convert to Skia data structures
69 const SkRect bufferRect = SkRect::MakeIWH(imgWidth, imgHeight);
70 SkRect skiaSrcRect = srcRect.toSkRect();
71 SkMatrix textureMatrix;
72 imgTransform.copyTo(textureMatrix);
73
74 // remove the y-flip applied to the matrix so that we can scale the srcRect.
75 // This flip is not needed as we specify the origin of the texture when we
76 // wrap it as an SkImage.
77 SkMatrix yFlip = SkMatrix::MakeScale(1, -1);
78 yFlip.postTranslate(0,1);
79 textureMatrix.preConcat(yFlip);
80
81 // copy the entire src if the rect is empty
82 if (skiaSrcRect.isEmpty()) {
83 skiaSrcRect = bufferRect;
84 }
85
86 // since the y-flip has been removed we can simply scale & translate
87 // the source rectangle
88 textureMatrix.mapRect(&skiaSrcRect);
89
90 if (skiaSrcRect.intersect(bufferRect)) {
91 SkPoint srcOrigin = SkPoint::Make(skiaSrcRect.fLeft, skiaSrcRect.fTop);
92
93 // if we need to scale the result we must render to an offscreen buffer
94 if (bitmap->width() != skiaSrcRect.width()
95 || bitmap->height() != skiaSrcRect.height()) {
96 sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
97 grContext.get(), SkBudgeted::kYes, bitmap->info());
98 SkPaint paint;
99 paint.setBlendMode(SkBlendMode::kSrc);
100 scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect,
101 SkRect::MakeWH(bitmap->width(), bitmap->height()), &paint);
102 image = scaledSurface->makeImageSnapshot();
103 srcOrigin.set(0,0);
104 }
105
106 if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
107 srcOrigin.fX, srcOrigin.fY)) {
108 copyResult = CopyResult::Success;
109 }
110 }
111 }
112
113 // make sure that we have deleted the texture (in the SkImage) before we
114 // destroy the EGLImage that it was created from
115 image.reset();
116 return copyResult;
117 }
118
119 } /* namespace skiapipeline */
120 } /* namespace uirenderer */
121 } /* namespace android */
122