blob: 1ba74f4a8abd73f05a475097f93c347f7a4175fb [file] [log] [blame]
/*
* 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