| /* | 
 |  * Copyright 2023 Google LLC | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "include/core/SkTypes.h" | 
 |  | 
 | #if defined(SK_GANESH) && defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26 | 
 |  | 
 | #include "include/android/SkImageAndroid.h" | 
 | #include "include/android/SkSurfaceAndroid.h" | 
 | #include "include/core/SkBitmap.h" | 
 | #include "include/core/SkCanvas.h" | 
 | #include "include/core/SkColorSpace.h" | 
 | #include "include/gpu/GrDirectContext.h" | 
 | #include "include/gpu/ganesh/SkSurfaceGanesh.h" | 
 | #include "tools/ToolUtils.h" | 
 | #include "tools/viewer/Slide.h" | 
 |  | 
 | #include <android/hardware_buffer.h> | 
 |  | 
 | namespace { | 
 |  | 
 | static void cleanup_resources(AHardwareBuffer* buffer) { | 
 |     if (buffer) { | 
 |         AHardwareBuffer_release(buffer); | 
 |     } | 
 | } | 
 |  | 
 | sk_sp<SkSurface> create_protected_AHB_surface(GrDirectContext* dContext, int width, int height) { | 
 |  | 
 |     AHardwareBuffer* buffer = nullptr; | 
 |  | 
 |     AHardwareBuffer_Desc hwbDesc; | 
 |     hwbDesc.width = width; | 
 |     hwbDesc.height = height; | 
 |     hwbDesc.layers = 1; | 
 |     hwbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | | 
 |                     AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER | | 
 |                     AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | | 
 |                     AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT; | 
 |  | 
 |     hwbDesc.usage |= AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT; | 
 |  | 
 |     hwbDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; | 
 |     // The following three are not used in the allocate | 
 |     hwbDesc.stride = 0; | 
 |     hwbDesc.rfu0= 0; | 
 |     hwbDesc.rfu1= 0; | 
 |  | 
 |     if (int error = AHardwareBuffer_allocate(&hwbDesc, &buffer)) { | 
 |         SkDebugf("Failed to allocated hardware buffer, error: %d\n", error); | 
 |         cleanup_resources(buffer); | 
 |         return nullptr; | 
 |     } | 
 |  | 
 |     sk_sp<SkSurface> surface = SkSurfaces::WrapAndroidHardwareBuffer(dContext, buffer, | 
 |                                                                      kTopLeft_GrSurfaceOrigin, | 
 |                                                                      nullptr, nullptr); | 
 |     if (!surface) { | 
 |         SkDebugf("Failed to make SkSurface.\n"); | 
 |         cleanup_resources(buffer); | 
 |         return nullptr; | 
 |     } | 
 |  | 
 |     return surface; | 
 | } | 
 |  | 
 | sk_sp<SkImage> create_protected_AHB_image(GrDirectContext* dContext, | 
 |                                           SkColor color, int width, int height) { | 
 |     sk_sp<SkSurface> surf = create_protected_AHB_surface(dContext, width, height); | 
 |     if (!surf) { | 
 |         return nullptr; | 
 |     } | 
 |  | 
 |     ToolUtils::draw_checkerboard(surf->getCanvas(), color, SK_ColorTRANSPARENT, 32); | 
 |  | 
 |     return surf->makeImageSnapshot(); | 
 | } | 
 |  | 
 | sk_sp<SkImage> create_unprotected_AHB_image(SkColor color, int width, int height) { | 
 |  | 
 |     const SkBitmap srcBitmap = ToolUtils::create_checkerboard_bitmap(width, height, color, | 
 |                                                                      SK_ColorTRANSPARENT, 32); | 
 |  | 
 |     AHardwareBuffer* buffer = nullptr; | 
 |  | 
 |     AHardwareBuffer_Desc hwbDesc; | 
 |     hwbDesc.width = width; | 
 |     hwbDesc.height = height; | 
 |     hwbDesc.layers = 1; | 
 |     hwbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | | 
 |                     AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; | 
 |  | 
 |     hwbDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; | 
 |     // The following three are not used in the allocate | 
 |     hwbDesc.stride = 0; | 
 |     hwbDesc.rfu0= 0; | 
 |     hwbDesc.rfu1= 0; | 
 |  | 
 |     if (int error = AHardwareBuffer_allocate(&hwbDesc, &buffer)) { | 
 |         SkDebugf("Failed to allocated hardware buffer, error: %d", error); | 
 |         cleanup_resources(buffer); | 
 |         return nullptr; | 
 |     } | 
 |  | 
 |     // Get actual desc for allocated buffer so we know the stride for uploading cpu data. | 
 |     AHardwareBuffer_describe(buffer, &hwbDesc); | 
 |  | 
 |     uint32_t* bufferAddr; | 
 |     if (AHardwareBuffer_lock(buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr, | 
 |                              reinterpret_cast<void**>(&bufferAddr))) { | 
 |         SkDebugf("Failed to lock hardware buffer"); | 
 |         cleanup_resources(buffer); | 
 |         return nullptr; | 
 |     } | 
 |  | 
 |     int bbp = srcBitmap.bytesPerPixel(); | 
 |     uint32_t* src = (uint32_t*)srcBitmap.getPixels(); | 
 |  | 
 |     uint32_t* dst = bufferAddr; | 
 |     for (int y = 0; y < height; ++y) { | 
 |         memcpy(dst, src, width * bbp); | 
 |         src += width; | 
 |         dst += hwbDesc.stride; | 
 |     } | 
 |     AHardwareBuffer_unlock(buffer, nullptr); | 
 |  | 
 |     return SkImages::DeferredFromAHardwareBuffer(buffer, kPremul_SkAlphaType, | 
 |                                                  /* colorSpace= */ nullptr, | 
 |                                                  kTopLeft_GrSurfaceOrigin); | 
 | } | 
 |  | 
 | sk_sp<SkImage> create_skia_image(GrDirectContext* dContext, int width, int height, | 
 |                                  SkColor color, bool isProtected) { | 
 |     SkImageInfo ii = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, | 
 |                                        kPremul_SkAlphaType); | 
 |  | 
 |     sk_sp<SkSurface> tmpSurface = SkSurfaces::RenderTarget(dContext, | 
 |                                                            skgpu::Budgeted::kYes, | 
 |                                                            ii, | 
 |                                                            /* sampleCount= */ 1, | 
 |                                                            kTopLeft_GrSurfaceOrigin, | 
 |                                                            /* surfaceProps= */ nullptr, | 
 |                                                            /* shouldCreateWithMips= */ false, | 
 |                                                            /* isProtected= */ true); | 
 |     if (!tmpSurface) { | 
 |         return nullptr; | 
 |     } | 
 |  | 
 |     ToolUtils::draw_checkerboard(tmpSurface->getCanvas(), color, SK_ColorTRANSPARENT, 32); | 
 |  | 
 |     return tmpSurface->makeImageSnapshot(); | 
 | } | 
 |  | 
 | } // anonymous namespace | 
 |  | 
 | class ProtectedSlide : public Slide { | 
 | public: | 
 |     ProtectedSlide() { fName = "Protected"; } | 
 |  | 
 |     SkISize getDimensions() const override { return {kSize, 2*kSize}; } | 
 |  | 
 |     void draw(SkCanvas* origCanvas) override { | 
 |         origCanvas->clear(SK_ColorDKGRAY); | 
 |  | 
 |         GrDirectContext* dContext = GrAsDirectContext(origCanvas->recordingContext()); | 
 |         if (!dContext || !dContext->supportsProtectedContent()) { | 
 |             origCanvas->clear(SK_ColorGREEN); | 
 |             return; | 
 |         } | 
 |  | 
 |         if (fCachedContext != dContext) { | 
 |             fCachedContext = dContext; | 
 |             fProtectedAHBImage = create_protected_AHB_image(dContext, SK_ColorRED, kSize, kSize); | 
 |             fUnprotectedAHBImage = create_unprotected_AHB_image(SK_ColorGREEN, kSize, kSize); | 
 |             fProtectedSkImage = create_skia_image(dContext, kSize, kSize, SK_ColorBLUE, | 
 |                                                   /* isProtected= */ true); | 
 |             fUnprotectedSkImage = create_skia_image(dContext, kSize, kSize, SK_ColorCYAN, | 
 |                                                     /* isProtected= */ false); | 
 |         } | 
 |  | 
 |         // Pick one of the four combinations to draw. Only the protected AHB-backed image will | 
 |         // reproduce the bug (b/242266174). | 
 |         SkImage* imgToUse = fProtectedAHBImage.get(); | 
 | //        SkImage* imgToUse = fUnprotectedAHBImage.get(); | 
 | //        SkImage* imgToUse = fProtectedSkImage.get(); | 
 | //        SkImage* imgToUse = fUnprotectedSkImage.get(); | 
 |  | 
 |         sk_sp<SkImage> indirectImg; | 
 |  | 
 |         { | 
 |             SkImageInfo ii = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, | 
 |                                                kPremul_SkAlphaType); | 
 |             sk_sp<SkSurface> tmpS = SkSurfaces::RenderTarget(dContext, | 
 |                                                              skgpu::Budgeted::kYes, | 
 |                                                              ii, | 
 |                                                              /* sampleCount= */ 1, | 
 |                                                              kTopLeft_GrSurfaceOrigin, | 
 |                                                              /* surfaceProps= */ nullptr, | 
 |                                                              /* shouldCreateWithMips= */ false, | 
 |                                                              /* isProtected= */ true); | 
 |  | 
 |             tmpS->getCanvas()->clear(SK_ColorMAGENTA); | 
 |             tmpS->getCanvas()->drawCircle(64, 64, 32, SkPaint()); | 
 |  | 
 |             // For protected AHB-backed images this draw seems to poison all above the draws too | 
 |             tmpS->getCanvas()->drawImage(imgToUse, 0, 0); | 
 |             indirectImg = tmpS->makeImageSnapshot(); | 
 |         } | 
 |  | 
 |         origCanvas->drawImage(imgToUse, 0, 0); | 
 |         origCanvas->drawImage(indirectImg, 0, kSize); | 
 |     } | 
 |  | 
 | private: | 
 |     static const int kSize = 128; | 
 |  | 
 |     GrDirectContext* fCachedContext = nullptr; | 
 |     sk_sp<SkImage> fProtectedAHBImage; | 
 |     sk_sp<SkImage> fUnprotectedAHBImage; | 
 |     sk_sp<SkImage> fProtectedSkImage; | 
 |     sk_sp<SkImage> fUnprotectedSkImage; | 
 | }; | 
 |  | 
 | DEF_SLIDE( return new ProtectedSlide(); ) | 
 |  | 
 | #endif |