Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
compressed_textures.cpp
Go to the documentation of this file.
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
9
10#if !defined(SK_BUILD_FOR_GOOGLE3) // Google3 doesn't have etc1.h
11
12#include "gm/gm.h"
16#include "include/core/SkData.h"
19#include "include/core/SkPath.h"
20#include "include/core/SkRect.h"
22#include "include/core/SkSize.h"
29#include "src/core/SkMipmap.h"
38
39#if defined(SK_GRAPHITE)
46#endif
47
48static SkPoint gen_pt(float angle, const SkVector& scale) {
49 SkScalar s = SkScalarSin(angle);
50 SkScalar c = SkScalarCos(angle);
51
52 return { scale.fX * c, scale.fY * s };
53}
54
55// The resulting path will be centered at (0,0) and its size will match 'dimensions'
56static SkPath make_gear(SkISize dimensions, int numTeeth) {
57 SkVector outerRad{ dimensions.fWidth / 2.0f, dimensions.fHeight / 2.0f };
58 SkVector innerRad{ dimensions.fWidth / 2.5f, dimensions.fHeight / 2.5f };
59 const float kAnglePerTooth = 2.0f * SK_ScalarPI / (3 * numTeeth);
60
61 float angle = 0.0f;
62
63 SkPath tmp;
65
66 tmp.moveTo(gen_pt(angle, outerRad));
67
68 for (int i = 0; i < numTeeth; ++i, angle += 3*kAnglePerTooth) {
69 tmp.lineTo(gen_pt(angle+kAnglePerTooth, outerRad));
70 tmp.lineTo(gen_pt(angle+(1.5f*kAnglePerTooth), innerRad));
71 tmp.lineTo(gen_pt(angle+(2.5f*kAnglePerTooth), innerRad));
72 tmp.lineTo(gen_pt(angle+(3.0f*kAnglePerTooth), outerRad));
73 }
74
75 tmp.close();
76
77 float fInnerRad = 0.1f * std::min(dimensions.fWidth, dimensions.fHeight);
78 if (fInnerRad > 0.5f) {
79 tmp.addCircle(0.0f, 0.0f, fInnerRad, SkPathDirection::kCCW);
80 }
81
82 return tmp;
83}
84
85// Render one level of a mipmap
87 SkPath path = make_gear(dimensions, 9);
88
89 SkImageInfo ii = SkImageInfo::Make(dimensions.width(), dimensions.height(),
92 SkBitmap bm;
93 bm.allocPixels(ii);
94
95 bm.eraseColor(opaque ? SK_ColorBLACK : SK_ColorTRANSPARENT);
96
97 SkCanvas c(bm);
98
100 paint.setColor(color | 0xFF000000);
101 paint.setAntiAlias(false);
102
103 c.translate(dimensions.width() / 2.0f, dimensions.height() / 2.0f);
104 c.drawPath(path, paint);
105
106 return bm;
107}
108
111#if defined(SK_GRAPHITE)
113#else
114 void* fGraphiteTexture = nullptr;
115#endif
116};
117
118// Create the compressed data blob needed to represent a mipmapped 2-color texture of the specified
119// compression format. In this case 2-color means either opaque black or transparent black plus
120// one other color.
121// Note that ETC1/ETC2_RGB8_UNORM only supports 565 opaque textures.
123 const SkISize dimensions,
125 bool opaque,
126 SkTextureCompressionType compression) {
127 size_t totalSize = SkCompressedDataSize(compression, dimensions, nullptr, true);
128
130 char* pixels = (char*) tmp->writable_data();
131
132 int numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
133
134 size_t offset = 0;
135
136 // Use a different color for each mipmap level so we can visually evaluate the draws
137 static const SkColor kColors[] = {
145 };
146
147 SkISize levelDims = dimensions;
148 for (int i = 0; i < numMipLevels; ++i) {
149 size_t levelSize = SkCompressedDataSize(compression, levelDims, nullptr, false);
150
151 SkBitmap bm = render_level(levelDims, kColors[i%7], colorType, opaque);
154 SkASSERT(opaque);
155
156 if (etc1_encode_image((unsigned char*)bm.getAddr16(0, 0),
157 bm.width(), bm.height(), 2, bm.rowBytes(),
158 (unsigned char*) &pixels[offset])) {
159 return {nullptr, nullptr};
160 }
161 } else {
162 GrTwoColorBC1Compress(bm.pixmap(), kColors[i%7], &pixels[offset]);
163 }
164
165 offset += levelSize;
166 levelDims = {std::max(1, levelDims.width()/2), std::max(1, levelDims.height()/2)};
167 }
168
170#if defined(SK_GRAPHITE)
171 skgpu::graphite::Recorder* recorder = canvas->recorder();
172 if (recorder) {
174 sk_gpu_test::ManagedGraphiteTexture::MakeFromCompressedData(recorder,
175 dimensions,
176 compression,
177 tmp,
178 skgpu::Mipmapped::kYes);
179 if (texture) {
180 image = SkImages::WrapTexture(recorder,
181 texture->texture(),
184 /*colorSpace=*/nullptr);
185 if (image) {
186 return {image, texture};
187 }
188 }
189 }
190#endif
191 auto dContext = GrAsDirectContext(canvas->recordingContext());
192 if (dContext) {
194 std::move(tmp),
195 dimensions.width(),
196 dimensions.height(),
197 compression,
198 skgpu::Mipmapped::kYes);
199 } else {
201 std::move(tmp), dimensions.width(), dimensions.height(), compression);
202 }
203 return {image, nullptr};
204}
205
206// Basic test of Ganesh's ETC1 and BC1 support
207// The layout is:
208// ETC2 BC1
209// --------------------------------------
210// RGB8 | kETC2_RGB8_UNORM | kBC1_RGB8_UNORM |
211// |--------------------------------------|
212// RGBA8 | | kBC1_RGBA8_UNORM |
213// --------------------------------------
214//
215// The nonPowerOfTwo and nonMultipleOfFour cases exercise some compression edge cases.
217public:
218 enum class Type {
219 kNormal,
222 };
223
225 this->setBGColor(0xFFCCCCCC);
226
227 switch (fType) {
229 // These dimensions force the top two mip levels to be 1x3 and 1x1
230 fImgDimensions.set(20, 60);
231 break;
233 // These dimensions force the top three mip levels to be 1x7, 1x3 and 1x1
234 fImgDimensions.set(13, 61); // prime numbers - just bc
235 break;
236 default:
237 fImgDimensions.set(kBaseTexWidth, kBaseTexHeight);
238 break;
239 }
240
241 }
242
243protected:
244 SkString getName() const override {
245 SkString name("compressed_textures");
246
247 if (fType == Type::kNonPowerOfTwo) {
248 name.append("_npot");
249 } else if (fType == Type::kNonMultipleOfFour) {
250 name.append("_nmof");
251 }
252
253 return name;
254 }
255
256 SkISize getISize() override {
257 return SkISize::Make(2*kCellWidth + 3*kPad, 2*kBaseTexHeight + 3*kPad);
258 }
259
262 auto dContext = GrAsDirectContext(canvas->recordingContext());
263 if (dContext && dContext->abandoned()) {
264 // This isn't a GpuGM so a null 'context' is okay but an abandoned context
265 // if forbidden.
266 return DrawResult::kSkip;
267 }
268
269 if (dContext &&
270 dContext->backend() == GrBackendApi::kDirect3D && fType == Type::kNonMultipleOfFour) {
271 // skbug.com/10541 - Are non-multiple-of-four BC1 textures supported in D3D?
272 return DrawResult::kSkip;
273 }
274
275 fOpaqueETC2Image = make_compressed_image(canvas, fImgDimensions,
278
279 fOpaqueBC1Image = make_compressed_image(canvas, fImgDimensions,
282
283 fTransparentBC1Image = make_compressed_image(canvas, fImgDimensions,
286
287 if (!fOpaqueETC2Image.fImage || !fOpaqueBC1Image.fImage || !fTransparentBC1Image.fImage) {
288 *errorMsg = "Failed to create compressed images.";
289 return DrawResult::kFail;
290 }
291
292 return DrawResult::kOk;
293 }
294
295 void onGpuTeardown() override {
296 fOpaqueETC2Image.fImage = nullptr;
297 fOpaqueBC1Image.fImage = nullptr;
298 fTransparentBC1Image.fImage = nullptr;
299 fOpaqueETC2Image.fGraphiteTexture = nullptr;
300 fOpaqueBC1Image.fGraphiteTexture = nullptr;
301 fTransparentBC1Image.fGraphiteTexture = nullptr;
302 }
303
304 void onDraw(SkCanvas* canvas) override {
305 this->drawCell(canvas, fOpaqueETC2Image.fImage.get(), { kPad, kPad });
306
307 this->drawCell(canvas, fOpaqueBC1Image.fImage.get(), { 2*kPad + kCellWidth, kPad });
308
309 this->drawCell(canvas, fTransparentBC1Image.fImage.get(),
310 { 2*kPad + kCellWidth, 2*kPad + kBaseTexHeight });
311 }
312
313private:
314 void drawCell(SkCanvas* canvas, SkImage* image, SkIVector offset) {
315
316 SkISize levelDimensions = fImgDimensions;
317 int numMipLevels = SkMipmap::ComputeLevelCount(levelDimensions.width(),
318 levelDimensions.height()) + 1;
319
321
322 bool isCompressed = false;
323 if (image->isTextureBacked()) {
324 auto dContext = GrAsDirectContext(canvas->recordingContext());
325 if (dContext) {
326 const GrCaps* caps = as_IB(image)->context()->priv().caps();
328 image, canvas->recordingContext());
329 isCompressed = caps->isFormatCompressed(proxy->backendFormat());
330 } else {
331 // Graphite has no fallback to upload the compressed data to a non-compressed
332 // format. So if the image is texture backed and graphite then it will be a
333 // compressed format.
334 isCompressed = true;
335 }
336 }
337
338 SkPaint redStrokePaint;
339 redStrokePaint.setColor(SK_ColorRED);
340 redStrokePaint.setStyle(SkPaint::kStroke_Style);
341
342 for (int i = 0; i < numMipLevels; ++i) {
344 levelDimensions.width(), levelDimensions.height());
345
346 canvas->drawImageRect(image, r, sampling);
347 if (!isCompressed) {
348 // Make it obvious which drawImages used decompressed images
349 canvas->drawRect(r, redStrokePaint);
350 }
351
352 if (i == 0) {
353 offset.fX += levelDimensions.width()+1;
354 } else {
355 offset.fY += levelDimensions.height()+1;
356 }
357
358 levelDimensions = {std::max(1, levelDimensions.width()/2),
359 std::max(1, levelDimensions.height()/2)};
360 }
361 }
362
363 static const int kPad = 8;
364 static const int kBaseTexWidth = 64;
365 static const int kCellWidth = 1.5f * kBaseTexWidth;
366 static const int kBaseTexHeight = 64;
367
368 Type fType;
369 SkISize fImgDimensions;
370
371
372 CompressedImageObjects fOpaqueETC2Image;
373 CompressedImageObjects fOpaqueBC1Image;
374 CompressedImageObjects fTransparentBC1Image;
375
376 using INHERITED = GM;
377};
378
379//////////////////////////////////////////////////////////////////////////////
380
384
385#endif
static GrDirectContext * GrAsDirectContext(GrContext_Base *base)
SkColor4f color
@ kOpaque_SkAlphaType
pixel is opaque
Definition SkAlphaType.h:28
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkASSERT(cond)
Definition SkAssert.h:116
SkColorType
Definition SkColorType.h:19
@ kRGB_565_SkColorType
pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word
Definition SkColorType.h:22
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
constexpr SkColor SK_ColorYELLOW
Definition SkColor.h:139
constexpr SkColor SK_ColorMAGENTA
Definition SkColor.h:147
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorCYAN
Definition SkColor.h:143
constexpr SkColor SK_ColorTRANSPARENT
Definition SkColor.h:99
constexpr SkColor SK_ColorBLUE
Definition SkColor.h:135
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
size_t SkCompressedDataSize(SkTextureCompressionType type, SkISize dimensions, TArray< size_t > *individualMipOffsets, bool mipmapped)
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
static SkImage_Base * as_IB(SkImage *image)
#define SkScalarSin(radians)
Definition SkScalar.h:45
#define SkScalarCos(radians)
Definition SkScalar.h:46
#define SK_ScalarPI
Definition SkScalar.h:21
DrawResult onGpuSetup(SkCanvas *canvas, SkString *errorMsg, GraphiteTestContext *graphiteTestContext) override
SkString getName() const override
SkISize getISize() override
void onDraw(SkCanvas *canvas) override
const GrCaps * caps() const
bool isFormatCompressed(const GrBackendFormat &format) const
Definition GrCaps.cpp:457
GrImageContextPriv priv()
const GrBackendFormat & backendFormat() const
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:258
int width() const
Definition SkBitmap.h:149
const SkPixmap & pixmap() const
Definition SkBitmap.h:133
size_t rowBytes() const
Definition SkBitmap.h:238
SkColorType colorType() const
Definition SkBitmap.h:160
int height() const
Definition SkBitmap.h:158
uint16_t * getAddr16(int x, int y) const
Definition SkBitmap.h:1265
void drawRect(const SkRect &rect, const SkPaint &paint)
void translate(SkScalar dx, SkScalar dy)
virtual GrRecordingContext * recordingContext() const
virtual skgpu::graphite::Recorder * recorder() const
void drawImageRect(const SkImage *, const SkRect &src, const SkRect &dst, const SkSamplingOptions &, const SkPaint *, SrcRectConstraint)
void drawPath(const SkPath &path, const SkPaint &paint)
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition SkData.cpp:116
virtual GrImageContext * context() const
virtual bool isTextureBacked() const =0
static int ComputeLevelCount(int baseWidth, int baseHeight)
Definition SkMipmap.cpp:134
void setStyle(Style style)
Definition SkPaint.cpp:105
void setColor(SkColor color)
Definition SkPaint.cpp:119
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
SkPath & addCircle(SkScalar x, SkScalar y, SkScalar radius, SkPathDirection dir=SkPathDirection::kCW)
Definition SkPath.cpp:1149
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
void setFillType(SkPathFillType ft)
Definition SkPath.h:235
SkPath & lineTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:718
SkPath & close()
Definition SkPath.cpp:813
T * get() const
Definition SkRefCnt.h:303
GM(SkColor backgroundColor=SK_ColorWHITE)
Definition gm.cpp:81
GraphiteTestContext * graphiteTestContext() const
Definition gm.h:228
void setBGColor(SkColor)
Definition gm.cpp:159
const Paint & paint
static SkPoint gen_pt(float angle, const SkVector &scale)
static SkPath make_gear(SkISize dimensions, int numTeeth)
SkBitmap render_level(SkISize dimensions, SkColor color, SkColorType colorType, bool opaque)
static CompressedImageObjects make_compressed_image(SkCanvas *canvas, const SkISize dimensions, SkColorType colorType, bool opaque, SkTextureCompressionType compression)
int etc1_encode_image(const etc1_byte *pIn, etc1_uint32 width, etc1_uint32 height, etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte *pOut)
Definition etc1.cpp:517
sk_sp< SkImage > image
Definition examples.cpp:29
float SkScalar
Definition extension.cpp:12
struct MyStruct s
const char * name
Definition fuchsia.cc:50
#define DEF_GM(CODE)
Definition gm.h:40
FlTexture * texture
SK_API sk_sp< SkImage > TextureFromCompressedTextureData(GrDirectContext *direct, sk_sp< SkData > data, int width, int height, SkTextureCompressionType type, skgpu::Mipmapped mipmapped=skgpu::Mipmapped::kNo, GrProtected isProtected=GrProtected::kNo)
SK_API sk_sp< SkImage > RasterFromCompressedTextureData(sk_sp< SkData > data, int width, int height, SkTextureCompressionType type)
SK_API sk_sp< SkImage > WrapTexture(skgpu::graphite::Recorder *, const skgpu::graphite::BackendTexture &, SkColorType colorType, SkAlphaType alphaType, sk_sp< SkColorSpace > colorSpace, skgpu::Origin origin, GenerateMipmapsFromBase generateMipmapsFromBase, TextureReleaseProc=nullptr, ReleaseContext=nullptr)
GrTextureProxy * GetTextureImageProxy(SkImage *image, GrRecordingContext *rContext)
static constexpr SkColorType CompressionTypeToSkColorType(SkTextureCompressionType compression)
DrawResult
Definition gm.h:104
const Scalar scale
Point offset
static constexpr SkCubicResampler Mitchell()
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
int32_t fHeight
Definition SkSize.h:18
int32_t fWidth
Definition SkSize.h:17
constexpr int32_t width() const
Definition SkSize.h:36
void set(int32_t w, int32_t h)
Definition SkSize.h:24
constexpr int32_t height() const
Definition SkSize.h:37
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659