Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2020 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "include/core/SkTypes.h" // IWYU pragma: keep |
| 9 | |
Robert Phillips | 3da9e94 | 2020-01-27 21:05:23 +0000 | [diff] [blame] | 10 | #if !defined(SK_BUILD_FOR_GOOGLE3) |
| 11 | |
Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 12 | #include "gm/gm.h" |
| 13 | #include "include/core/SkBitmap.h" |
| 14 | #include "include/core/SkCanvas.h" |
| 15 | #include "include/core/SkColor.h" |
| 16 | #include "include/core/SkData.h" |
| 17 | #include "include/core/SkImage.h" |
| 18 | #include "include/core/SkImageInfo.h" |
| 19 | #include "include/core/SkPath.h" |
| 20 | #include "include/core/SkRect.h" |
| 21 | #include "include/core/SkRefCnt.h" |
| 22 | #include "include/core/SkSize.h" |
| 23 | #include "include/core/SkString.h" |
| 24 | #include "src/core/SkMipMap.h" |
| 25 | #include "src/gpu/GrDataUtils.h" |
| 26 | #include "third_party/etc1/etc1.h" |
| 27 | |
| 28 | class GrContext; |
| 29 | class GrRenderTargetContext; |
| 30 | |
| 31 | static SkPoint gen_pt(float angle, const SkVector& scale) { |
| 32 | SkScalar s = SkScalarSin(angle); |
| 33 | SkScalar c = SkScalarCos(angle); |
| 34 | |
| 35 | return { scale.fX * c, scale.fY * s }; |
| 36 | } |
| 37 | |
| 38 | // The resulting path will be centered at (0,0) and its size will match 'dimensions' |
| 39 | static SkPath make_gear(SkISize dimensions, int numTeeth) { |
| 40 | SkVector outerRad{ dimensions.fWidth / 2.0f, dimensions.fHeight / 2.0f }; |
| 41 | SkVector innerRad{ dimensions.fWidth / 2.5f, dimensions.fHeight / 2.5f }; |
| 42 | const float kAnglePerTooth = SK_ScalarPI / numTeeth; |
| 43 | |
| 44 | float angle = 0.0f; |
| 45 | |
| 46 | SkPath tmp; |
| 47 | tmp.setFillType(SkPathFillType::kWinding); |
| 48 | |
| 49 | tmp.moveTo(gen_pt(angle, outerRad)); |
| 50 | |
| 51 | for (int i = 0; i < numTeeth; ++i, angle += 2*kAnglePerTooth) { |
| 52 | tmp.lineTo(gen_pt(angle+kAnglePerTooth, outerRad)); |
| 53 | tmp.lineTo(gen_pt(angle+kAnglePerTooth, innerRad)); |
| 54 | tmp.lineTo(gen_pt(angle+2*kAnglePerTooth, innerRad)); |
| 55 | tmp.lineTo(gen_pt(angle+2*kAnglePerTooth, outerRad)); |
| 56 | } |
| 57 | |
| 58 | tmp.close(); |
| 59 | |
| 60 | float fInnerRad = 0.1f * SkTMin(dimensions.fWidth, dimensions.fHeight); |
| 61 | if (fInnerRad > 0.5f) { |
| 62 | tmp.addCircle(0.0f, 0.0f, fInnerRad, SkPathDirection::kCCW); |
| 63 | } |
| 64 | |
| 65 | return tmp; |
| 66 | } |
| 67 | |
| 68 | // Render one level of a mipmap |
| 69 | SkBitmap render_level(SkISize dimensions, SkColor color, SkColorType colorType, bool opaque) { |
| 70 | SkPath path = make_gear(dimensions, 9); |
| 71 | |
| 72 | SkImageInfo ii = SkImageInfo::Make(dimensions.width(), dimensions.height(), |
| 73 | colorType, opaque ? kOpaque_SkAlphaType |
| 74 | : kPremul_SkAlphaType); |
| 75 | SkBitmap bm; |
| 76 | bm.allocPixels(ii); |
| 77 | |
| 78 | bm.eraseColor(opaque ? SK_ColorBLACK : SK_ColorTRANSPARENT); |
| 79 | |
| 80 | SkCanvas c(bm); |
| 81 | |
| 82 | SkPaint paint; |
| 83 | paint.setColor(color | 0xFF000000); |
| 84 | paint.setAntiAlias(false); |
| 85 | |
| 86 | c.translate(dimensions.width() / 2.0f, dimensions.height() / 2.0f); |
| 87 | c.drawPath(path, paint); |
| 88 | |
| 89 | return bm; |
| 90 | } |
| 91 | |
| 92 | // Create the compressed data blob needed to represent a mipmapped 2-color texture of the specified |
| 93 | // compression format. In this case 2-color means either opaque black or transparent black plus |
| 94 | // one other color. |
| 95 | // Note that ETC1/ETC2_RGB8_UNORM only supports 565 opaque textures. |
| 96 | static sk_sp<SkData> make_compressed_data(SkISize dimensions, |
| 97 | SkColorType colorType, |
| 98 | bool opaque, |
| 99 | SkImage::CompressionType compression) { |
Robert Phillips | 3da9e94 | 2020-01-27 21:05:23 +0000 | [diff] [blame] | 100 | size_t totalSize = GrCompressedDataSize(compression, dimensions, nullptr, GrMipMapped::kYes); |
Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 101 | |
| 102 | sk_sp<SkData> tmp = SkData::MakeUninitialized(totalSize); |
| 103 | char* pixels = (char*) tmp->writable_data(); |
| 104 | |
| 105 | int numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1; |
| 106 | |
| 107 | size_t offset = 0; |
| 108 | |
| 109 | // Use a different color for each mipmap level so we can visually evaluate the draws |
| 110 | static const SkColor kColors[] = { |
| 111 | SK_ColorRED, |
| 112 | SK_ColorGREEN, |
| 113 | SK_ColorBLUE, |
| 114 | SK_ColorCYAN, |
| 115 | SK_ColorMAGENTA, |
| 116 | SK_ColorYELLOW, |
| 117 | SK_ColorWHITE, |
| 118 | }; |
| 119 | |
| 120 | for (int i = 0; i < numMipLevels; ++i) { |
Robert Phillips | 3da9e94 | 2020-01-27 21:05:23 +0000 | [diff] [blame] | 121 | size_t levelSize = GrCompressedDataSize(compression, dimensions, nullptr, GrMipMapped::kNo); |
Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 122 | |
| 123 | SkBitmap bm = render_level(dimensions, kColors[i%7], colorType, opaque); |
| 124 | if (compression == SkImage::CompressionType::kETC2_RGB8_UNORM) { |
| 125 | SkASSERT(bm.colorType() == kRGB_565_SkColorType); |
| 126 | SkASSERT(opaque); |
| 127 | |
| 128 | if (etc1_encode_image((unsigned char*)bm.getAddr16(0, 0), |
| 129 | bm.width(), bm.height(), 2, bm.rowBytes(), |
| 130 | (unsigned char*) &pixels[offset])) { |
| 131 | return nullptr; |
| 132 | } |
| 133 | } else { |
| 134 | GrTwoColorBC1Compress(bm.pixmap(), kColors[i%7], &pixels[offset]); |
| 135 | } |
| 136 | |
| 137 | offset += levelSize; |
| 138 | dimensions = {SkTMax(1, dimensions.width()/2), SkTMax(1, dimensions.height()/2)}; |
| 139 | } |
| 140 | |
| 141 | return tmp; |
| 142 | } |
| 143 | |
| 144 | // Basic test of Ganesh's ETC1 and BC1 support |
| 145 | // The layout is: |
| 146 | // ETC2 BC1 |
| 147 | // -------------------------------------- |
| 148 | // RGB8 | kETC2_RGB8_UNORM | kBC1_RGB8_UNORM | |
| 149 | // |--------------------------------------| |
| 150 | // RGBA8 | | kBC1_RGBA8_UNORM | |
| 151 | // -------------------------------------- |
| 152 | // |
Robert Phillips | 3da9e94 | 2020-01-27 21:05:23 +0000 | [diff] [blame] | 153 | class CompressedTexturesGM : public skiagm::GpuGM { |
Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 154 | public: |
| 155 | CompressedTexturesGM() { |
| 156 | this->setBGColor(0xFFCCCCCC); |
| 157 | } |
| 158 | |
| 159 | protected: |
| 160 | SkString onShortName() override { |
| 161 | return SkString("compressed_textures"); |
| 162 | } |
| 163 | |
| 164 | SkISize onISize() override { |
| 165 | return SkISize::Make(2*kCellWidth + 3*kPad, 2*kTexHeight + 3*kPad); |
| 166 | } |
| 167 | |
| 168 | void onOnceBeforeDraw() override { |
| 169 | fOpaqueETC2Data = make_compressed_data({ kTexWidth, kTexHeight }, |
| 170 | kRGB_565_SkColorType, true, |
| 171 | SkImage::CompressionType::kETC2_RGB8_UNORM); |
| 172 | |
| 173 | fOpaqueBC1Data = make_compressed_data({ kTexWidth, kTexHeight }, |
| 174 | kRGBA_8888_SkColorType, true, |
| 175 | SkImage::CompressionType::kBC1_RGB8_UNORM); |
Robert Phillips | b085527 | 2020-01-15 12:56:52 -0500 | [diff] [blame] | 176 | |
| 177 | fTransparentBC1Data = make_compressed_data({ kTexWidth, kTexHeight }, |
| 178 | kRGBA_8888_SkColorType, false, |
| 179 | SkImage::CompressionType::kBC1_RGBA8_UNORM); |
Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 180 | } |
| 181 | |
Robert Phillips | 3da9e94 | 2020-01-27 21:05:23 +0000 | [diff] [blame] | 182 | void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override { |
Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 183 | this->drawCell(context, canvas, fOpaqueETC2Data, |
| 184 | SkImage::CompressionType::kETC2_RGB8_UNORM, { kPad, kPad }); |
| 185 | |
| 186 | this->drawCell(context, canvas, fOpaqueBC1Data, |
| 187 | SkImage::CompressionType::kBC1_RGB8_UNORM, { 2*kPad + kCellWidth, kPad }); |
Robert Phillips | b085527 | 2020-01-15 12:56:52 -0500 | [diff] [blame] | 188 | |
| 189 | this->drawCell(context, canvas, fTransparentBC1Data, |
| 190 | SkImage::CompressionType::kBC1_RGBA8_UNORM, |
| 191 | { 2*kPad + kCellWidth, 2*kPad + kTexHeight }); |
Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 192 | } |
| 193 | |
| 194 | private: |
| 195 | void drawCell(GrContext* context, SkCanvas* canvas, sk_sp<SkData> data, |
| 196 | SkImage::CompressionType compression, SkIVector offset) { |
| 197 | |
Robert Phillips | 3da9e94 | 2020-01-27 21:05:23 +0000 | [diff] [blame] | 198 | sk_sp<SkImage> image = SkImage::MakeFromCompressed(context, data, |
| 199 | kTexWidth, kTexHeight, |
| 200 | compression, GrMipMapped::kYes); |
Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 201 | SkISize dimensions{ kTexWidth, kTexHeight }; |
| 202 | |
| 203 | int numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1; |
| 204 | |
| 205 | SkPaint paint; |
| 206 | paint.setFilterQuality(kHigh_SkFilterQuality); // to force mipmapping |
| 207 | |
| 208 | for (int i = 0; i < numMipLevels; ++i) { |
| 209 | SkRect r = SkRect::MakeXYWH(offset.fX, offset.fY, |
| 210 | dimensions.width(), dimensions.height()); |
| 211 | |
| 212 | canvas->drawImageRect(image, r, &paint); |
| 213 | |
| 214 | if (i == 0) { |
| 215 | offset.fX += dimensions.width(); |
| 216 | } else { |
| 217 | offset.fY += dimensions.height(); |
| 218 | } |
| 219 | |
| 220 | dimensions = {SkTMax(1, dimensions.width()/2), SkTMax(1, dimensions.height()/2)}; |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | static const int kPad = 8; |
| 225 | static const int kTexWidth = 64; |
| 226 | static const int kCellWidth = 1.5f * kTexWidth; |
| 227 | static const int kTexHeight = 64; |
| 228 | |
| 229 | sk_sp<SkData> fOpaqueETC2Data; |
| 230 | sk_sp<SkData> fOpaqueBC1Data; |
Robert Phillips | b085527 | 2020-01-15 12:56:52 -0500 | [diff] [blame] | 231 | sk_sp<SkData> fTransparentBC1Data; |
Robert Phillips | ac90802 | 2020-01-14 16:54:17 -0500 | [diff] [blame] | 232 | |
| 233 | typedef GM INHERITED; |
| 234 | }; |
| 235 | |
| 236 | ////////////////////////////////////////////////////////////////////////////// |
| 237 | |
| 238 | DEF_GM(return new CompressedTexturesGM;) |
| 239 | |
Robert Phillips | 3da9e94 | 2020-01-27 21:05:23 +0000 | [diff] [blame] | 240 | #endif |