blob: f8e6ae3c80e05bf28fabc20043346a65e65f850b [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 Phillips3da9e942020-01-27 21:05:23 +000010#if !defined(SK_BUILD_FOR_GOOGLE3)
11
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"
24#include "src/core/SkMipMap.h"
25#include "src/gpu/GrDataUtils.h"
26#include "third_party/etc1/etc1.h"
27
28class GrContext;
29class GrRenderTargetContext;
30
31static 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'
39static 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
69SkBitmap 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.
96static sk_sp<SkData> make_compressed_data(SkISize dimensions,
97 SkColorType colorType,
98 bool opaque,
99 SkImage::CompressionType compression) {
Robert Phillips3da9e942020-01-27 21:05:23 +0000100 size_t totalSize = GrCompressedDataSize(compression, dimensions, nullptr, GrMipMapped::kYes);
Robert Phillipsac908022020-01-14 16:54:17 -0500101
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 Phillips3da9e942020-01-27 21:05:23 +0000121 size_t levelSize = GrCompressedDataSize(compression, dimensions, nullptr, GrMipMapped::kNo);
Robert Phillipsac908022020-01-14 16:54:17 -0500122
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 Phillips3da9e942020-01-27 21:05:23 +0000153class CompressedTexturesGM : public skiagm::GpuGM {
Robert Phillipsac908022020-01-14 16:54:17 -0500154public:
155 CompressedTexturesGM() {
156 this->setBGColor(0xFFCCCCCC);
157 }
158
159protected:
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 Phillipsb0855272020-01-15 12:56:52 -0500176
177 fTransparentBC1Data = make_compressed_data({ kTexWidth, kTexHeight },
178 kRGBA_8888_SkColorType, false,
179 SkImage::CompressionType::kBC1_RGBA8_UNORM);
Robert Phillipsac908022020-01-14 16:54:17 -0500180 }
181
Robert Phillips3da9e942020-01-27 21:05:23 +0000182 void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
Robert Phillipsac908022020-01-14 16:54:17 -0500183 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 Phillipsb0855272020-01-15 12:56:52 -0500188
189 this->drawCell(context, canvas, fTransparentBC1Data,
190 SkImage::CompressionType::kBC1_RGBA8_UNORM,
191 { 2*kPad + kCellWidth, 2*kPad + kTexHeight });
Robert Phillipsac908022020-01-14 16:54:17 -0500192 }
193
194private:
195 void drawCell(GrContext* context, SkCanvas* canvas, sk_sp<SkData> data,
196 SkImage::CompressionType compression, SkIVector offset) {
197
Robert Phillips3da9e942020-01-27 21:05:23 +0000198 sk_sp<SkImage> image = SkImage::MakeFromCompressed(context, data,
199 kTexWidth, kTexHeight,
200 compression, GrMipMapped::kYes);
Robert Phillipsac908022020-01-14 16:54:17 -0500201 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 Phillipsb0855272020-01-15 12:56:52 -0500231 sk_sp<SkData> fTransparentBC1Data;
Robert Phillipsac908022020-01-14 16:54:17 -0500232
233 typedef GM INHERITED;
234};
235
236//////////////////////////////////////////////////////////////////////////////
237
238DEF_GM(return new CompressedTexturesGM;)
239
Robert Phillips3da9e942020-01-27 21:05:23 +0000240#endif