Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf91788e authored by Stan Iliev's avatar Stan Iliev
Browse files

Fix PixelCopy readback

Apply correct texture transform and use filter if source and dest
have different size.

Test: Ran and passed CtsUiRenderingTestCases,
Test: CtsGraphicsTestCases, CtsViewTestCases tests and the
Test: DecodeAccuracyTest.
Bug:68051015
Change-Id: Iee885e243e43497c9294f7475c0c5b9c1a783754
parent 1eb19a3b
Loading
Loading
Loading
Loading
+33 −40
Original line number Diff line number Diff line
@@ -84,57 +84,50 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4
    sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture,
            kTopLeft_GrSurfaceOrigin));
    if (image) {
        // Convert imgTransform matrix from right to left handed coordinate system.
        // If we have a matrix transformation in right handed coordinate system
        //|ScaleX, SkewX,  TransX| same transform in left handed is    |ScaleX, SkewX,   TransX  |
        //|SkewY,  ScaleY, TransY|                                     |-SkewY, -ScaleY, 1-TransY|
        //|0,      0,      1     |                                     |0,      0,       1       |
        SkMatrix textureMatrix;
        textureMatrix.setIdentity();
        textureMatrix[SkMatrix::kMScaleX] = imgTransform[Matrix4::kScaleX];
        textureMatrix[SkMatrix::kMScaleY] = -imgTransform[Matrix4::kScaleY];
        textureMatrix[SkMatrix::kMSkewX] = imgTransform[Matrix4::kSkewX];
        textureMatrix[SkMatrix::kMSkewY] = -imgTransform[Matrix4::kSkewY];
        textureMatrix[SkMatrix::kMTransX] = imgTransform[Matrix4::kTranslateX];
        textureMatrix[SkMatrix::kMTransY] = 1-imgTransform[Matrix4::kTranslateY];

        // textureMatrix maps 2D texture coordinates of the form (s, t, 1) with s and t in the
        // inclusive range [0, 1] to the texture (see GLConsumer::getTransformMatrix comments).
        // Convert textureMatrix to translate in real texture dimensions. Texture width and
        // height are affected by the orientation (width and height swapped for 90/270 rotation).
        if (textureMatrix[SkMatrix::kMSkewX] >= 0.5f || textureMatrix[SkMatrix::kMSkewX] <= -0.5f) {
            textureMatrix[SkMatrix::kMTransX] *= imgHeight;
            textureMatrix[SkMatrix::kMTransY] *= imgWidth;
        } else {
            textureMatrix[SkMatrix::kMTransX] *= imgWidth;
            textureMatrix[SkMatrix::kMTransY] *= imgHeight;
        int displayedWidth = imgWidth, displayedHeight = imgHeight;
        // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
        // size.
        if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
            std::swap(displayedWidth, displayedHeight);
        }

        // convert to Skia data structures
        SkRect skiaSrcRect = srcRect.toSkRect();
        SkMatrix textureMatrixInv;
        SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
        bool srcNotEmpty = false;
        if (textureMatrix.invert(&textureMatrixInv)) {
        SkRect skiaSrcRect = srcRect.toSkRect();
        if (skiaSrcRect.isEmpty()) {
                skiaSrcRect = SkRect::MakeIWH(imgWidth, imgHeight);
                srcNotEmpty = !skiaSrcRect.isEmpty();
            } else {
                // src and dest rectangles need to be converted into texture coordinates before the
                // rotation matrix is applied (because drawImageRect preconcat its matrix).
                textureMatrixInv.mapRect(&skiaSrcRect);
                srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(imgWidth, imgHeight));
            }
            textureMatrixInv.mapRect(&skiaDestRect);
            skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
        }
        bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));

        if (srcNotEmpty) {
            SkMatrix textureMatrixInv;
            imgTransform.copyTo(textureMatrixInv);
            //TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
            // use bottom left origin and remove flipV and invert transformations.
            SkMatrix flipV;
            flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
            textureMatrixInv.preConcat(flipV);
            textureMatrixInv.preScale(1.0f/displayedWidth, 1.0f/displayedHeight);
            textureMatrixInv.postScale(imgWidth, imgHeight);
            SkMatrix textureMatrix;
            if (!textureMatrixInv.invert(&textureMatrix)) {
                textureMatrix = textureMatrixInv;
            }

            textureMatrixInv.mapRect(&skiaSrcRect);
            textureMatrixInv.mapRect(&skiaDestRect);

            // we render in an offscreen buffer to scale and to avoid an issue b/62262733
            // with reading incorrect data from EGLImage backed SkImage (likely a driver bug)
            sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
                    grContext.get(), SkBudgeted::kYes, bitmap->info());
            SkPaint paint;
            paint.setBlendMode(SkBlendMode::kSrc);
            // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
            // is codified by tests using golden images like DecodeAccuracyTest.
            if (skiaSrcRect.width() != bitmap->width()
                    || skiaSrcRect.height() != bitmap->height()) {
                //TODO: apply filter always, but check if tests will be fine
                paint.setFilterQuality(kLow_SkFilterQuality);
            }
            scaledSurface->getCanvas()->concat(textureMatrix);
            scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint,
                    SkCanvas::kFast_SrcRectConstraint);