302e8fb771
We still occasionally downcast, so this is not airtight, but it (1) allows us to know where we are downcasting and (2) lets us move away from GrContext (and hopefully remove it sooner than later.) All three canaries are currently broken =( so here we go! Bug: skia:104662 Change-Id: I84efe132574690b62ea512e194e4f9e318e9c050 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/316218 Commit-Queue: Adlai Holler <adlai@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
219 lines
7.3 KiB
C++
219 lines
7.3 KiB
C++
/*
|
|
* Copyright 2020 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "gm/gm.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkImage.h"
|
|
#include "include/gpu/GrDirectContext.h"
|
|
#include "include/gpu/GrRecordingContext.h"
|
|
#include "src/core/SkCompressedDataUtils.h"
|
|
#include "src/gpu/GrCaps.h"
|
|
#include "src/gpu/GrImageContextPriv.h"
|
|
#include "src/image/SkImage_Base.h"
|
|
#include "src/image/SkImage_GpuBase.h"
|
|
|
|
constexpr int kImgWidth = 16;
|
|
constexpr int kImgHeight = 8;
|
|
constexpr int kPad = 4;
|
|
|
|
struct BC1Block {
|
|
uint16_t fColor0;
|
|
uint16_t fColor1;
|
|
uint32_t fIndices;
|
|
};
|
|
|
|
static int num_4x4_blocks(int size) {
|
|
return ((size + 3) & ~3) >> 2;
|
|
}
|
|
|
|
static uint16_t to565(SkColor col) {
|
|
int r5 = SkMulDiv255Round(31, SkColorGetR(col));
|
|
int g6 = SkMulDiv255Round(63, SkColorGetG(col));
|
|
int b5 = SkMulDiv255Round(31, SkColorGetB(col));
|
|
|
|
return (r5 << 11) | (g6 << 5) | b5;
|
|
}
|
|
|
|
// BC1 has per-block transparency. If, taken as ints,
|
|
// fColor0 < fColor1 -> the block has transparency (& it is in color3)
|
|
// fColor1 > fColor0 -> the block is opaque
|
|
//
|
|
// This method can create two blocks to test out BC1's behavior. If BC1
|
|
// behaves as expected (i.e., w/ per-block transparency) then, for RGBA textures,
|
|
// the transparent block(s) should appear as:
|
|
// opaque black, medium grey, transparent black, white.
|
|
// and the opaque block(s) should appear as:
|
|
// opaque black, dark grey, light grey, white
|
|
//
|
|
// For RGB textures, however, the transparent block(s) should appear as:
|
|
// opaque black, medium grey, _opaque_ black, white
|
|
// and the opaque block(s) should appear as:
|
|
// opaque black, dark grey, light grey, white.
|
|
static void create_BC1_block(BC1Block* block, bool transparent) {
|
|
unsigned int byte;
|
|
|
|
if (transparent) {
|
|
block->fColor0 = to565(SK_ColorBLACK);
|
|
block->fColor1 = to565(SK_ColorWHITE);
|
|
SkASSERT(block->fColor0 <= block->fColor1); // this signals a transparent block
|
|
// opaque black (col0), medium grey (col2), transparent black (col3), white (col1).
|
|
byte = (0x0 << 0) | (0x2 << 2) | (0x3 << 4) | (0x1 << 6);
|
|
} else {
|
|
block->fColor0 = to565(SK_ColorWHITE);
|
|
block->fColor1 = to565(SK_ColorBLACK);
|
|
SkASSERT(block->fColor0 > block->fColor1); // this signals an opaque block
|
|
// opaque black (col1), dark grey (col3), light grey (col2), white (col0)
|
|
byte = (0x1 << 0) | (0x3 << 2) | (0x2 << 4) | (0x0 << 6);
|
|
}
|
|
|
|
block->fIndices = (byte << 24) | (byte << 16) | (byte << 8) | byte;
|
|
}
|
|
|
|
// This makes a 16x8 BC1 texture which has the top 4 rows be officially transparent
|
|
// and the bottom 4 rows be officially opaque.
|
|
static sk_sp<SkData> make_compressed_data() {
|
|
SkISize dim{ kImgWidth, kImgHeight };
|
|
|
|
size_t totalSize = SkCompressedDataSize(SkImage::CompressionType::kBC1_RGB8_UNORM, dim,
|
|
nullptr, false);
|
|
|
|
sk_sp<SkData> tmp = SkData::MakeUninitialized(totalSize);
|
|
BC1Block* dstBlocks = reinterpret_cast<BC1Block*>(tmp->writable_data());
|
|
|
|
BC1Block transBlock, opaqueBlock;
|
|
create_BC1_block(&transBlock, true);
|
|
create_BC1_block(&opaqueBlock, false);
|
|
|
|
int numXBlocks = num_4x4_blocks(kImgWidth);
|
|
int numYBlocks = num_4x4_blocks(kImgHeight);
|
|
|
|
for (int y = 0; y < numYBlocks; ++y) {
|
|
for (int x = 0; x < numXBlocks; ++x) {
|
|
dstBlocks[y*numXBlocks + x] = (y < numYBlocks/2) ? transBlock : opaqueBlock;
|
|
}
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
|
|
static sk_sp<SkImage> data_to_img(GrDirectContext *direct, sk_sp<SkData> data,
|
|
SkImage::CompressionType compression) {
|
|
if (direct) {
|
|
return SkImage::MakeTextureFromCompressed(direct, std::move(data),
|
|
kImgWidth,
|
|
kImgHeight,
|
|
compression,
|
|
GrMipmapped::kNo);
|
|
} else {
|
|
return SkImage::MakeRasterFromCompressed(std::move(data),
|
|
kImgWidth,
|
|
kImgHeight,
|
|
compression);
|
|
}
|
|
}
|
|
|
|
static void draw_image(SkCanvas* canvas, sk_sp<SkImage> image, int x, int y) {
|
|
|
|
bool isCompressed = false;
|
|
if (image && image->isTextureBacked()) {
|
|
const GrCaps* caps = as_IB(image)->context()->priv().caps();
|
|
|
|
GrTextureProxy* proxy = as_IB(image)->peekProxy();
|
|
isCompressed = caps->isFormatCompressed(proxy->backendFormat());
|
|
}
|
|
|
|
canvas->drawImage(image, x, y);
|
|
|
|
if (!isCompressed) {
|
|
SkRect r = SkRect::MakeXYWH(x, y, kImgWidth, kImgHeight);
|
|
r.outset(1.0f, 1.0f);
|
|
|
|
SkPaint redStroke;
|
|
redStroke.setColor(SK_ColorRED);
|
|
redStroke.setStyle(SkPaint::kStroke_Style);
|
|
redStroke.setStrokeWidth(2.0f);
|
|
|
|
canvas->drawRect(r, redStroke);
|
|
}
|
|
}
|
|
|
|
namespace skiagm {
|
|
|
|
// This GM draws the BC1 compressed texture filled with "make_compressed_data"s data twice.
|
|
//
|
|
// It is drawn once (on the top) as a kBC1_RGB8_UNORM texture and then again (on the bottom)
|
|
// as a kBC1_RGBA8_UNORM texture.
|
|
//
|
|
// If BC1 behaves as expected we should see:
|
|
//
|
|
// RGB8 Black MidGrey Black* White ...
|
|
// Black DrkGrey LtGrey White ...
|
|
//
|
|
// RGBA8 Black MidGrey Green+ White ...
|
|
// Black DrkGrey LtGrey White ...
|
|
//
|
|
// * We expect this to be black bc the transparent black will be forced to opaque. If BC1 were
|
|
// treating it as an opaque block then it would be LtGrey - not black.
|
|
// + This is just the background showing through the transparent black
|
|
class BC1TransparencyGM : public GM {
|
|
public:
|
|
BC1TransparencyGM() {
|
|
this->setBGColor(SK_ColorGREEN);
|
|
}
|
|
|
|
protected:
|
|
|
|
SkString onShortName() override {
|
|
return SkString("bc1_transparency");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(kImgWidth + 2 * kPad, 2 * kImgHeight + 3 * kPad);
|
|
}
|
|
|
|
DrawResult onGpuSetup(GrDirectContext* dContext, SkString* errorMsg) override {
|
|
if (dContext && dContext->abandoned()) {
|
|
// This isn't a GpuGM so a null 'context' is okay but an abandoned context
|
|
// if forbidden.
|
|
return DrawResult::kSkip;
|
|
}
|
|
|
|
sk_sp<SkData> bc1Data = make_compressed_data();
|
|
|
|
fRGBImage = data_to_img(dContext, bc1Data, SkImage::CompressionType::kBC1_RGB8_UNORM);
|
|
fRGBAImage = data_to_img(dContext, std::move(bc1Data),
|
|
SkImage::CompressionType::kBC1_RGBA8_UNORM);
|
|
if (!fRGBImage || !fRGBAImage) {
|
|
*errorMsg = "Failed to create BC1 images.";
|
|
return DrawResult::kFail;
|
|
}
|
|
|
|
return DrawResult::kOk;
|
|
}
|
|
|
|
void onGpuTeardown() override {
|
|
fRGBImage = nullptr;
|
|
fRGBAImage = nullptr;
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
draw_image(canvas, fRGBImage, kPad, kPad);
|
|
draw_image(canvas, fRGBAImage, kPad, 2 * kPad + kImgHeight);
|
|
}
|
|
|
|
private:
|
|
sk_sp<SkImage> fRGBImage;
|
|
sk_sp<SkImage> fRGBAImage;
|
|
|
|
using INHERITED = GM;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
DEF_GM(return new BC1TransparencyGM;)
|
|
} // namespace skiagm
|