| /* |
| * 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_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26 |
| |
| #include "include/core/SkBitmap.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkColorSpace.h" |
| #include "tools/ToolUtils.h" |
| #include "tools/viewer/Slide.h" |
| |
| #if defined(SK_GANESH) |
| #include "include/android/SkImageAndroid.h" |
| #include "include/android/SkSurfaceAndroid.h" |
| #include "include/gpu/GrDirectContext.h" |
| #include "include/gpu/ganesh/SkSurfaceGanesh.h" |
| #endif |
| |
| #if defined(SK_GRAPHITE) |
| #include "include/android/graphite/SurfaceAndroid.h" |
| #include "include/gpu/graphite/Context.h" |
| #include "include/gpu/graphite/Surface.h" |
| #include "src/gpu/graphite/RecorderPriv.h" |
| |
| #else |
| namespace skgpu::graphite { |
| class Recorder; |
| } |
| #endif |
| |
| #include <android/hardware_buffer.h> |
| |
| using namespace skgpu::graphite; |
| |
| namespace { |
| |
| static void release_buffer(AHardwareBuffer* buffer) { |
| if (buffer) { |
| AHardwareBuffer_release(buffer); |
| } |
| } |
| |
| sk_sp<SkSurface> wrap_buffer(GrDirectContext* dContext, |
| Recorder* recorder, |
| AHardwareBuffer* buffer) { |
| #if defined(SK_GANESH) |
| if (dContext) { |
| return SkSurfaces::WrapAndroidHardwareBuffer(dContext, |
| buffer, |
| kTopLeft_GrSurfaceOrigin, |
| /* colorSpace= */ nullptr, |
| /* surfaceProps= */ nullptr); |
| } |
| #endif |
| |
| #if defined(SK_GRAPHITE) |
| if (recorder) { |
| return SkSurfaces::WrapAndroidHardwareBuffer(recorder, |
| buffer, |
| /* colorSpace= */ nullptr, |
| /* surfaceProps= */ nullptr); |
| } |
| #endif |
| |
| return nullptr; |
| } |
| |
| sk_sp<SkSurface> create_protected_render_target(GrDirectContext* dContext, |
| Recorder* recorder, |
| const SkImageInfo& ii) { |
| #if defined(SK_GANESH) |
| if (dContext) { |
| return SkSurfaces::RenderTarget(dContext, |
| skgpu::Budgeted::kYes, |
| ii, |
| /* sampleCount= */ 1, |
| kTopLeft_GrSurfaceOrigin, |
| /* surfaceProps= */ nullptr, |
| /* shouldCreateWithMips= */ false, |
| /* isProtected= */ true); |
| } |
| #endif |
| |
| #if defined(SK_GRAPHITE) |
| if (recorder) { |
| // Protected-ness is pulled off of the recorder |
| return SkSurfaces::RenderTarget(recorder, |
| ii, |
| skgpu::Mipmapped::kNo, |
| /* props= */ nullptr); |
| } |
| #endif |
| |
| return nullptr; |
| } |
| |
| AHardwareBuffer* create_protected_buffer(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); |
| release_buffer(buffer); |
| return nullptr; |
| } |
| |
| return buffer; |
| } |
| |
| sk_sp<SkImage> create_protected_AHB_image(GrDirectContext* dContext, |
| Recorder* recorder, |
| AHardwareBuffer* buffer, |
| SkColor color) { |
| |
| sk_sp<SkSurface> surf = wrap_buffer(dContext, recorder, buffer); |
| if (!surf) { |
| SkDebugf("Failed to make SkSurface.\n"); |
| return nullptr; |
| } |
| |
| ToolUtils::draw_checkerboard(surf->getCanvas(), color, SK_ColorTRANSPARENT, 32); |
| |
| return surf->makeImageSnapshot(); |
| } |
| |
| sk_sp<SkImage> create_protected_skia_image(GrDirectContext* dContext, |
| Recorder* recorder, |
| int width, int height, |
| SkColor color) { |
| SkImageInfo ii = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, |
| kPremul_SkAlphaType); |
| |
| sk_sp<SkSurface> tmpSurface = create_protected_render_target(dContext, recorder, ii); |
| 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); |
| |
| skgpu::graphite::Recorder* recorder = origCanvas->recorder(); |
| GrDirectContext* dContext = GrAsDirectContext(origCanvas->recordingContext()); |
| |
| #if defined(SK_GANESH) |
| if (dContext && !dContext->supportsProtectedContent()) { |
| origCanvas->clear(SK_ColorGREEN); |
| return; |
| } |
| #endif |
| |
| #if defined(SK_GRAPHITE) |
| if (recorder && recorder->priv().isProtected() == skgpu::Protected::kNo) { |
| origCanvas->clear(SK_ColorBLUE); |
| return; |
| } |
| #endif |
| |
| if (!dContext && !recorder) { |
| origCanvas->clear(SK_ColorRED); |
| return; |
| } |
| |
| AHardwareBuffer* buffer = create_protected_buffer(kSize, kSize); |
| |
| sk_sp<SkImage> protectedAHBImage = create_protected_AHB_image(dContext, recorder, buffer, |
| SK_ColorRED); |
| sk_sp<SkImage> protectedSkImage = create_protected_skia_image(dContext, recorder, |
| kSize, kSize, SK_ColorBLUE); |
| |
| // Pick one of the two protected images to draw. Only the protected AHB-backed image will |
| // reproduce the bug (b/242266174). |
| SkImage* imgToUse = protectedAHBImage.get(); |
| // SkImage* imgToUse = protectedSkImage.get(); |
| |
| sk_sp<SkImage> indirectImg; |
| |
| { |
| SkImageInfo ii = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, |
| kPremul_SkAlphaType); |
| sk_sp<SkSurface> tmpS = create_protected_render_target(dContext, recorder, ii); |
| |
| 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); |
| |
| protectedAHBImage.reset(); |
| protectedSkImage.reset(); |
| release_buffer(buffer); |
| } |
| |
| private: |
| static const int kSize = 128; |
| }; |
| |
| DEF_SLIDE( return new ProtectedSlide(); ) |
| |
| #endif |