blob: 9982a0cfe2bfc2aec8f9b8f55b6bc4b74318c03d [file] [log] [blame]
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -05001/*
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
Derek Sollenberger4170db32017-08-09 13:52:36 -040019#include "DeviceInfo.h"
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050020#include "Matrix.h"
21#include "Properties.h"
22#include <SkCanvas.h>
23#include <SkSurface.h>
Greg Danielac2d2322017-07-12 11:30:15 -040024#include <GrBackendSurface.h>
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050025#include <gl/GrGLInterface.h>
26#include <gl/GrGLTypes.h>
27#include <GLES2/gl2.h>
28#include <GLES2/gl2ext.h>
29
30using namespace android::uirenderer::renderthread;
31
32namespace android {
33namespace uirenderer {
34namespace skiapipeline {
35
36CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
37 int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) {
38
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.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend,
49 (GrBackendContext)glInterface.get()));
50 } else {
51 grContext->resetContext();
52 }
53
54 GrGLTextureInfo externalTexture;
55 externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
56 externalTexture.fID = sourceTexId;
57
Stan Iliev08fc19a2017-07-24 10:20:33 -040058 GrPixelConfig pixelConfig;
59 switch (bitmap->colorType()) {
60 case kRGBA_F16_SkColorType:
61 pixelConfig = kRGBA_half_GrPixelConfig;
62 break;
63 case kN32_SkColorType:
64 default:
65 pixelConfig = kRGBA_8888_GrPixelConfig;
66 break;
67 }
68
Derek Sollenberger4170db32017-08-09 13:52:36 -040069 /* Ideally, we would call grContext->caps()->isConfigRenderable(...). We
70 * currently can't do that since some devices (i.e. SwiftShader) supports all
71 * the appropriate half float extensions, but only allow the buffer to be read
72 * back as full floats. We can relax this extension if Skia implements support
73 * for reading back float buffers (skbug.com/6945).
74 */
75 if (pixelConfig == kRGBA_half_GrPixelConfig &&
76 !DeviceInfo::get()->extensions().hasFloatTextures()) {
77 ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
78 return CopyResult::DestinationInvalid;
79 }
80
Stan Iliev08fc19a2017-07-24 10:20:33 -040081 GrBackendTexture backendTexture(imgWidth, imgHeight, pixelConfig, externalTexture);
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050082
83 CopyResult copyResult = CopyResult::UnknownError;
Greg Danielac2d2322017-07-12 11:30:15 -040084 sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture,
85 kTopLeft_GrSurfaceOrigin));
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050086 if (image) {
Stan Iliev1220a2a2017-07-19 18:09:25 -040087 // Convert imgTransform matrix from right to left handed coordinate system.
88 // If we have a matrix transformation in right handed coordinate system
89 //|ScaleX, SkewX, TransX| same transform in left handed is |ScaleX, SkewX, TransX |
90 //|SkewY, ScaleY, TransY| |-SkewY, -ScaleY, 1-TransY|
91 //|0, 0, 1 | |0, 0, 1 |
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050092 SkMatrix textureMatrix;
Stan Iliev1220a2a2017-07-19 18:09:25 -040093 textureMatrix.setIdentity();
94 textureMatrix[SkMatrix::kMScaleX] = imgTransform[Matrix4::kScaleX];
95 textureMatrix[SkMatrix::kMScaleY] = -imgTransform[Matrix4::kScaleY];
96 textureMatrix[SkMatrix::kMSkewX] = imgTransform[Matrix4::kSkewX];
97 textureMatrix[SkMatrix::kMSkewY] = -imgTransform[Matrix4::kSkewY];
98 textureMatrix[SkMatrix::kMTransX] = imgTransform[Matrix4::kTranslateX];
99 textureMatrix[SkMatrix::kMTransY] = 1-imgTransform[Matrix4::kTranslateY];
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500100
Stan Iliev1220a2a2017-07-19 18:09:25 -0400101 // textureMatrix maps 2D texture coordinates of the form (s, t, 1) with s and t in the
102 // inclusive range [0, 1] to the texture (see GLConsumer::getTransformMatrix comments).
103 // Convert textureMatrix to translate in real texture dimensions. Texture width and
104 // height are affected by the orientation (width and height swapped for 90/270 rotation).
105 if (textureMatrix[SkMatrix::kMSkewX] >= 0.5f || textureMatrix[SkMatrix::kMSkewX] <= -0.5f) {
106 textureMatrix[SkMatrix::kMTransX] *= imgHeight;
107 textureMatrix[SkMatrix::kMTransY] *= imgWidth;
108 } else {
109 textureMatrix[SkMatrix::kMTransX] *= imgWidth;
110 textureMatrix[SkMatrix::kMTransY] *= imgHeight;
111 }
Stan Ilievdf6520e2017-07-17 18:50:16 -0400112
113 // convert to Skia data structures
114 SkRect skiaSrcRect = srcRect.toSkRect();
115 SkMatrix textureMatrixInv;
116 SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
117 bool srcNotEmpty = false;
118 if (textureMatrix.invert(&textureMatrixInv)) {
119 if (skiaSrcRect.isEmpty()) {
120 skiaSrcRect = SkRect::MakeIWH(imgWidth, imgHeight);
121 srcNotEmpty = !skiaSrcRect.isEmpty();
122 } else {
123 // src and dest rectangles need to be converted into texture coordinates before the
124 // rotation matrix is applied (because drawImageRect preconcat its matrix).
125 textureMatrixInv.mapRect(&skiaSrcRect);
126 srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(imgWidth, imgHeight));
127 }
128 textureMatrixInv.mapRect(&skiaDestRect);
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500129 }
130
Stan Ilievdf6520e2017-07-17 18:50:16 -0400131 if (srcNotEmpty) {
Stan Iliev7bc3bc62017-05-24 13:28:36 -0400132 // we render in an offscreen buffer to scale and to avoid an issue b/62262733
133 // with reading incorrect data from EGLImage backed SkImage (likely a driver bug)
134 sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
135 grContext.get(), SkBudgeted::kYes, bitmap->info());
136 SkPaint paint;
137 paint.setBlendMode(SkBlendMode::kSrc);
Stan Ilievdf6520e2017-07-17 18:50:16 -0400138 scaledSurface->getCanvas()->concat(textureMatrix);
Derek Sollenberger6c2a9e22017-08-15 16:23:01 -0400139 scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint,
140 SkCanvas::kFast_SrcRectConstraint);
Stan Ilievdf6520e2017-07-17 18:50:16 -0400141
Stan Iliev7bc3bc62017-05-24 13:28:36 -0400142 image = scaledSurface->makeImageSnapshot();
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500143
Stan Iliev7bc3bc62017-05-24 13:28:36 -0400144 if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
John Reckabbedfc2017-07-06 15:27:23 -0700145 bitmap->notifyPixelsChanged();
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500146 copyResult = CopyResult::Success;
147 }
148 }
149 }
150
151 // make sure that we have deleted the texture (in the SkImage) before we
152 // destroy the EGLImage that it was created from
153 image.reset();
154 return copyResult;
155}
156
157} /* namespace skiapipeline */
158} /* namespace uirenderer */
159} /* namespace android */