Home | History | Annotate | Download | only in skia
      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 
     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