blob: 3ae4d746df8c58d4552d6e41b5843a74abdd6800 [file] [log] [blame]
Robert Phillipsac908022020-01-14 16:54:17 -05001/*
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 Phillips99dead92020-01-27 16:11:57 -050010#if !defined(SK_BUILD_FOR_GOOGLE3) // Google3 doesn't have etc1.h
Robert Phillips3da9e942020-01-27 21:05:23 +000011
Robert Phillipsac908022020-01-14 16:54:17 -050012#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"
Robert Phillips99dead92020-01-27 16:11:57 -050024#include "src/core/SkCompressedDataUtils.h"
Robert Phillipsac908022020-01-14 16:54:17 -050025#include "src/core/SkMipMap.h"
Robert Phillips99dead92020-01-27 16:11:57 -050026#include "src/gpu/GrContextPriv.h"
Robert Phillipsac908022020-01-14 16:54:17 -050027#include "src/gpu/GrDataUtils.h"
Robert Phillips99dead92020-01-27 16:11:57 -050028#include "src/image/SkImage_Base.h"
Robert Phillipsac908022020-01-14 16:54:17 -050029#include "third_party/etc1/etc1.h"
30
31class GrContext;
32class GrRenderTargetContext;
33
34static SkPoint gen_pt(float angle, const SkVector& scale) {
35 SkScalar s = SkScalarSin(angle);
36 SkScalar c = SkScalarCos(angle);
37
38 return { scale.fX * c, scale.fY * s };
39}
40
41// The resulting path will be centered at (0,0) and its size will match 'dimensions'
42static SkPath make_gear(SkISize dimensions, int numTeeth) {
43 SkVector outerRad{ dimensions.fWidth / 2.0f, dimensions.fHeight / 2.0f };
44 SkVector innerRad{ dimensions.fWidth / 2.5f, dimensions.fHeight / 2.5f };
Robert Phillips162e04b2020-01-28 14:22:43 -050045 const float kAnglePerTooth = 2.0f * SK_ScalarPI / (3 * numTeeth);
Robert Phillipsac908022020-01-14 16:54:17 -050046
47 float angle = 0.0f;
48
49 SkPath tmp;
50 tmp.setFillType(SkPathFillType::kWinding);
51
52 tmp.moveTo(gen_pt(angle, outerRad));
53
Robert Phillips162e04b2020-01-28 14:22:43 -050054 for (int i = 0; i < numTeeth; ++i, angle += 3*kAnglePerTooth) {
Robert Phillipsac908022020-01-14 16:54:17 -050055 tmp.lineTo(gen_pt(angle+kAnglePerTooth, outerRad));
Robert Phillips162e04b2020-01-28 14:22:43 -050056 tmp.lineTo(gen_pt(angle+(1.5f*kAnglePerTooth), innerRad));
57 tmp.lineTo(gen_pt(angle+(2.5f*kAnglePerTooth), innerRad));
58 tmp.lineTo(gen_pt(angle+(3.0f*kAnglePerTooth), outerRad));
Robert Phillipsac908022020-01-14 16:54:17 -050059 }
60
61 tmp.close();
62
63 float fInnerRad = 0.1f * SkTMin(dimensions.fWidth, dimensions.fHeight);
64 if (fInnerRad > 0.5f) {
65 tmp.addCircle(0.0f, 0.0f, fInnerRad, SkPathDirection::kCCW);
66 }
67
68 return tmp;
69}
70
71// Render one level of a mipmap
72SkBitmap render_level(SkISize dimensions, SkColor color, SkColorType colorType, bool opaque) {
73 SkPath path = make_gear(dimensions, 9);
74
75 SkImageInfo ii = SkImageInfo::Make(dimensions.width(), dimensions.height(),
76 colorType, opaque ? kOpaque_SkAlphaType
77 : kPremul_SkAlphaType);
78 SkBitmap bm;
79 bm.allocPixels(ii);
80
81 bm.eraseColor(opaque ? SK_ColorBLACK : SK_ColorTRANSPARENT);
82
83 SkCanvas c(bm);
84
85 SkPaint paint;
86 paint.setColor(color | 0xFF000000);
87 paint.setAntiAlias(false);
88
89 c.translate(dimensions.width() / 2.0f, dimensions.height() / 2.0f);
90 c.drawPath(path, paint);
91
92 return bm;
93}
94
95// Create the compressed data blob needed to represent a mipmapped 2-color texture of the specified
96// compression format. In this case 2-color means either opaque black or transparent black plus
97// one other color.
98// Note that ETC1/ETC2_RGB8_UNORM only supports 565 opaque textures.
99static sk_sp<SkData> make_compressed_data(SkISize dimensions,
100 SkColorType colorType,
101 bool opaque,
102 SkImage::CompressionType compression) {
Robert Phillips99dead92020-01-27 16:11:57 -0500103 size_t totalSize = SkCompressedDataSize(compression, dimensions, nullptr, true);
Robert Phillipsac908022020-01-14 16:54:17 -0500104
105 sk_sp<SkData> tmp = SkData::MakeUninitialized(totalSize);
106 char* pixels = (char*) tmp->writable_data();
107
108 int numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
109
110 size_t offset = 0;
111
112 // Use a different color for each mipmap level so we can visually evaluate the draws
113 static const SkColor kColors[] = {
114 SK_ColorRED,
115 SK_ColorGREEN,
116 SK_ColorBLUE,
117 SK_ColorCYAN,
118 SK_ColorMAGENTA,
119 SK_ColorYELLOW,
120 SK_ColorWHITE,
121 };
122
123 for (int i = 0; i < numMipLevels; ++i) {
Robert Phillips99dead92020-01-27 16:11:57 -0500124 size_t levelSize = SkCompressedDataSize(compression, dimensions, nullptr, false);
Robert Phillipsac908022020-01-14 16:54:17 -0500125
126 SkBitmap bm = render_level(dimensions, kColors[i%7], colorType, opaque);
127 if (compression == SkImage::CompressionType::kETC2_RGB8_UNORM) {
128 SkASSERT(bm.colorType() == kRGB_565_SkColorType);
129 SkASSERT(opaque);
130
131 if (etc1_encode_image((unsigned char*)bm.getAddr16(0, 0),
132 bm.width(), bm.height(), 2, bm.rowBytes(),
133 (unsigned char*) &pixels[offset])) {
134 return nullptr;
135 }
136 } else {
137 GrTwoColorBC1Compress(bm.pixmap(), kColors[i%7], &pixels[offset]);
138 }
139
140 offset += levelSize;
141 dimensions = {SkTMax(1, dimensions.width()/2), SkTMax(1, dimensions.height()/2)};
142 }
143
144 return tmp;
145}
146
147// Basic test of Ganesh's ETC1 and BC1 support
148// The layout is:
149// ETC2 BC1
150// --------------------------------------
151// RGB8 | kETC2_RGB8_UNORM | kBC1_RGB8_UNORM |
152// |--------------------------------------|
153// RGBA8 | | kBC1_RGBA8_UNORM |
154// --------------------------------------
155//
Robert Phillips162e04b2020-01-28 14:22:43 -0500156// The nonPowerOfTwo and nonMultipleOfFour cases exercise some compression edge cases.
Robert Phillips99dead92020-01-27 16:11:57 -0500157class CompressedTexturesGM : public skiagm::GM {
Robert Phillipsac908022020-01-14 16:54:17 -0500158public:
Robert Phillips162e04b2020-01-28 14:22:43 -0500159 enum class Type {
160 kNormal,
161 kNonPowerOfTwo,
162 kNonMultipleOfFour
163 };
164
165 CompressedTexturesGM(Type type) : fType(type) {
Robert Phillipsac908022020-01-14 16:54:17 -0500166 this->setBGColor(0xFFCCCCCC);
Robert Phillips162e04b2020-01-28 14:22:43 -0500167
168 switch (fType) {
169 case Type::kNonPowerOfTwo:
170 // These dimensions force the top two mip levels to be 1x3 and 1x1
171 fImgDimensions.set(20, 60);
172 break;
173 case Type::kNonMultipleOfFour:
174 // These dimensions force the top three mip levels to be 1x7, 1x3 and 1x1
175 fImgDimensions.set(13, 61); // prime numbers - just bc
176 break;
177 default:
178 fImgDimensions.set(kBaseTexWidth, kBaseTexHeight);
179 break;
180 }
181
Robert Phillipsac908022020-01-14 16:54:17 -0500182 }
183
184protected:
185 SkString onShortName() override {
Robert Phillips162e04b2020-01-28 14:22:43 -0500186 SkString name("compressed_textures");
187
188 if (fType == Type::kNonPowerOfTwo) {
189 name.append("_npot");
190 } else if (fType == Type::kNonMultipleOfFour) {
191 name.append("_nmof");
192 }
193
194 return name;
Robert Phillipsac908022020-01-14 16:54:17 -0500195 }
196
197 SkISize onISize() override {
Robert Phillips162e04b2020-01-28 14:22:43 -0500198 return SkISize::Make(2*kCellWidth + 3*kPad, 2*kBaseTexHeight + 3*kPad);
Robert Phillipsac908022020-01-14 16:54:17 -0500199 }
200
201 void onOnceBeforeDraw() override {
Robert Phillips162e04b2020-01-28 14:22:43 -0500202 fOpaqueETC2Data = make_compressed_data(fImgDimensions, kRGB_565_SkColorType, true,
Robert Phillipsac908022020-01-14 16:54:17 -0500203 SkImage::CompressionType::kETC2_RGB8_UNORM);
204
Robert Phillips162e04b2020-01-28 14:22:43 -0500205 fOpaqueBC1Data = make_compressed_data(fImgDimensions, kRGBA_8888_SkColorType, true,
Robert Phillipsac908022020-01-14 16:54:17 -0500206 SkImage::CompressionType::kBC1_RGB8_UNORM);
Robert Phillipsb0855272020-01-15 12:56:52 -0500207
Robert Phillips162e04b2020-01-28 14:22:43 -0500208 fTransparentBC1Data = make_compressed_data(fImgDimensions, kRGBA_8888_SkColorType, false,
Robert Phillipsb0855272020-01-15 12:56:52 -0500209 SkImage::CompressionType::kBC1_RGBA8_UNORM);
Robert Phillipsac908022020-01-14 16:54:17 -0500210 }
211
Robert Phillips99dead92020-01-27 16:11:57 -0500212 void onDraw(SkCanvas* canvas) override {
213 GrContext* context = canvas->getGrContext();
214
Robert Phillipsac908022020-01-14 16:54:17 -0500215 this->drawCell(context, canvas, fOpaqueETC2Data,
216 SkImage::CompressionType::kETC2_RGB8_UNORM, { kPad, kPad });
217
218 this->drawCell(context, canvas, fOpaqueBC1Data,
219 SkImage::CompressionType::kBC1_RGB8_UNORM, { 2*kPad + kCellWidth, kPad });
Robert Phillipsb0855272020-01-15 12:56:52 -0500220
221 this->drawCell(context, canvas, fTransparentBC1Data,
222 SkImage::CompressionType::kBC1_RGBA8_UNORM,
Robert Phillips162e04b2020-01-28 14:22:43 -0500223 { 2*kPad + kCellWidth, 2*kPad + kBaseTexHeight });
Robert Phillipsac908022020-01-14 16:54:17 -0500224 }
225
226private:
227 void drawCell(GrContext* context, SkCanvas* canvas, sk_sp<SkData> data,
228 SkImage::CompressionType compression, SkIVector offset) {
229
Robert Phillips99dead92020-01-27 16:11:57 -0500230 sk_sp<SkImage> image;
231 if (context) {
Robert Phillips45c94872020-01-28 15:59:31 -0500232 image = SkImage::MakeTextureFromCompressed(context, std::move(data),
233 fImgDimensions.width(),
234 fImgDimensions.height(),
235 compression, GrMipMapped::kYes);
Robert Phillips99dead92020-01-27 16:11:57 -0500236 } else {
Robert Phillips162e04b2020-01-28 14:22:43 -0500237 image = SkImage::MakeRasterFromCompressed(std::move(data),
238 fImgDimensions.width(),
239 fImgDimensions.height(),
Robert Phillips99dead92020-01-27 16:11:57 -0500240 compression);
241 }
242 if (!image) {
243 return;
244 }
245
Robert Phillips162e04b2020-01-28 14:22:43 -0500246 SkISize levelDimensions = fImgDimensions;
247 int numMipLevels = SkMipMap::ComputeLevelCount(levelDimensions.width(),
248 levelDimensions.height()) + 1;
Robert Phillipsac908022020-01-14 16:54:17 -0500249
Robert Phillipse0735522020-01-31 11:03:32 -0500250 SkPaint imagePaint;
251 imagePaint.setFilterQuality(kHigh_SkFilterQuality); // to force mipmapping
Robert Phillipsac908022020-01-14 16:54:17 -0500252
Robert Phillips99dead92020-01-27 16:11:57 -0500253 bool isCompressed = false;
254 if (image->isTextureBacked()) {
255 const GrCaps* caps = context->priv().caps();
256
257 GrTextureProxy* proxy = as_IB(image)->peekProxy();
258 isCompressed = caps->isFormatCompressed(proxy->backendFormat());
259 }
260
Robert Phillipse0735522020-01-31 11:03:32 -0500261 SkPaint redStrokePaint;
262 redStrokePaint.setColor(SK_ColorRED);
263 redStrokePaint.setStyle(SkPaint::kStroke_Style);
Robert Phillips99dead92020-01-27 16:11:57 -0500264
Robert Phillipsac908022020-01-14 16:54:17 -0500265 for (int i = 0; i < numMipLevels; ++i) {
266 SkRect r = SkRect::MakeXYWH(offset.fX, offset.fY,
Robert Phillips162e04b2020-01-28 14:22:43 -0500267 levelDimensions.width(), levelDimensions.height());
Robert Phillipsac908022020-01-14 16:54:17 -0500268
Robert Phillipse0735522020-01-31 11:03:32 -0500269 canvas->drawImageRect(image, r, &imagePaint);
270 if (!isCompressed) {
271 // Make it obvious which drawImages used decompressed images
272 canvas->drawRect(r, redStrokePaint);
273 }
Robert Phillipsac908022020-01-14 16:54:17 -0500274
275 if (i == 0) {
Robert Phillips162e04b2020-01-28 14:22:43 -0500276 offset.fX += levelDimensions.width()+1;
Robert Phillipsac908022020-01-14 16:54:17 -0500277 } else {
Robert Phillips162e04b2020-01-28 14:22:43 -0500278 offset.fY += levelDimensions.height()+1;
Robert Phillipsac908022020-01-14 16:54:17 -0500279 }
280
Robert Phillips162e04b2020-01-28 14:22:43 -0500281 levelDimensions = {SkTMax(1, levelDimensions.width()/2),
282 SkTMax(1, levelDimensions.height()/2)};
Robert Phillipsac908022020-01-14 16:54:17 -0500283 }
284 }
285
286 static const int kPad = 8;
Robert Phillips162e04b2020-01-28 14:22:43 -0500287 static const int kBaseTexWidth = 64;
288 static const int kCellWidth = 1.5f * kBaseTexWidth;
289 static const int kBaseTexHeight = 64;
Robert Phillipsac908022020-01-14 16:54:17 -0500290
Robert Phillips162e04b2020-01-28 14:22:43 -0500291 Type fType;
292 SkISize fImgDimensions;
Robert Phillipsac908022020-01-14 16:54:17 -0500293 sk_sp<SkData> fOpaqueETC2Data;
294 sk_sp<SkData> fOpaqueBC1Data;
Robert Phillipsb0855272020-01-15 12:56:52 -0500295 sk_sp<SkData> fTransparentBC1Data;
Robert Phillipsac908022020-01-14 16:54:17 -0500296
297 typedef GM INHERITED;
298};
299
300//////////////////////////////////////////////////////////////////////////////
301
Robert Phillips162e04b2020-01-28 14:22:43 -0500302DEF_GM(return new CompressedTexturesGM(CompressedTexturesGM::Type::kNormal);)
303DEF_GM(return new CompressedTexturesGM(CompressedTexturesGM::Type::kNonPowerOfTwo);)
304DEF_GM(return new CompressedTexturesGM(CompressedTexturesGM::Type::kNonMultipleOfFour);)
Robert Phillipsac908022020-01-14 16:54:17 -0500305
Robert Phillips3da9e942020-01-27 21:05:23 +0000306#endif