Create mipmaps when creating images
Follow-on CLs: - add serialization for mips to pictures - implement for other image types (e.g. lazy(?), gpu) - improve generated mip quality Bug: skia:10411 Change-Id: Id874f170e9cb8ae4405dc4f6249e1ea6274f20aa Reviewed-on: https://skia-review.googlesource.com/c/skia/+/297739 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
parent
12c5d29ba6
commit
2fe1569298
@ -320,29 +320,38 @@ class ShowMipLevels3 : public skiagm::GM {
|
|||||||
|
|
||||||
SkString onShortName() override { return SkString("showmiplevels_explicit"); }
|
SkString onShortName() override { return SkString("showmiplevels_explicit"); }
|
||||||
|
|
||||||
SkISize onISize() override { return {1290, 990}; }
|
SkISize onISize() override { return {1130, 970}; }
|
||||||
|
|
||||||
SkScalar draw_downscaling(SkCanvas* canvas, SkFilterOptions options) {
|
|
||||||
SkAutoCanvasRestore acr(canvas, true);
|
|
||||||
|
|
||||||
SkPaint paint;
|
|
||||||
SkRect r = {0, 0, 150, 150};
|
|
||||||
for (float scale = 1; scale >= 0.125f; scale *= 0.75f) {
|
|
||||||
SkMatrix matrix = SkMatrix::Scale(scale, scale);
|
|
||||||
paint.setShader(fImg->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
|
|
||||||
options, &matrix));
|
|
||||||
canvas->drawRect(r, paint);
|
|
||||||
// r.offset(r.width() + 10, 0);
|
|
||||||
canvas->translate(r.width() + 10, 0);
|
|
||||||
}
|
|
||||||
return 160;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onOnceBeforeDraw() override {
|
void onOnceBeforeDraw() override {
|
||||||
fImg = GetResourceAsImage("images/ship.png");
|
fImg = GetResourceAsImage("images/ship.png");
|
||||||
|
fImg = fImg->makeRasterImage(); // makeWithMips only works on raster for now
|
||||||
|
|
||||||
|
const SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
|
||||||
|
|
||||||
|
SkMipmapBuilder builder(fImg->imageInfo());
|
||||||
|
for (int i = 0; i < builder.countLevels(); ++i) {
|
||||||
|
auto surf = SkSurface::MakeRasterDirect(builder.level(i));
|
||||||
|
surf->getCanvas()->drawColor(colors[i % SK_ARRAY_COUNT(colors)]);
|
||||||
|
}
|
||||||
|
fImg = fImg->withMipmaps(builder.detach());
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDraw(SkCanvas* canvas) override {
|
DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
|
||||||
|
auto is_recording_canvas = [](SkCanvas* canvas) {
|
||||||
|
SkPixmap pm;
|
||||||
|
if (canvas->peekPixels(&pm)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (canvas->getGrContext()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if (is_recording_canvas(canvas)) {
|
||||||
|
// TODO: make explicit mipmaps serialize
|
||||||
|
return DrawResult::kSkip;
|
||||||
|
}
|
||||||
|
|
||||||
canvas->drawColor(0xFFDDDDDD);
|
canvas->drawColor(0xFFDDDDDD);
|
||||||
|
|
||||||
const SkSamplingMode samplings[] = {
|
const SkSamplingMode samplings[] = {
|
||||||
@ -357,11 +366,26 @@ class ShowMipLevels3 : public skiagm::GM {
|
|||||||
for (auto sa : samplings) {
|
for (auto sa : samplings) {
|
||||||
canvas->translate(0, draw_downscaling(canvas, {sa, mm}));
|
canvas->translate(0, draw_downscaling(canvas, {sa, mm}));
|
||||||
}
|
}
|
||||||
canvas->translate(0, 10);
|
|
||||||
}
|
}
|
||||||
|
return DrawResult::kOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SkScalar draw_downscaling(SkCanvas* canvas, SkFilterOptions options) {
|
||||||
|
SkAutoCanvasRestore acr(canvas, true);
|
||||||
|
|
||||||
|
SkPaint paint;
|
||||||
|
SkRect r = {0, 0, 150, 150};
|
||||||
|
for (float scale = 1; scale >= 0.1f; scale *= 0.7f) {
|
||||||
|
SkMatrix matrix = SkMatrix::Scale(scale, scale);
|
||||||
|
paint.setShader(fImg->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
|
||||||
|
options, &matrix));
|
||||||
|
canvas->drawRect(r, paint);
|
||||||
|
canvas->translate(r.width() + 10, 0);
|
||||||
|
}
|
||||||
|
return r.height() + 10;
|
||||||
|
}
|
||||||
|
|
||||||
typedef skiagm::GM INHERITED;
|
typedef skiagm::GM INHERITED;
|
||||||
};
|
};
|
||||||
DEF_GM( return new ShowMipLevels3; )
|
DEF_GM( return new ShowMipLevels3; )
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "include/core/SkTileMode.h"
|
#include "include/core/SkTileMode.h"
|
||||||
|
|
||||||
struct SkMask;
|
struct SkMask;
|
||||||
|
class SkMipMap;
|
||||||
struct SkIRect;
|
struct SkIRect;
|
||||||
struct SkRect;
|
struct SkRect;
|
||||||
class SkPaint;
|
class SkPaint;
|
||||||
@ -1165,7 +1166,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
sk_sp<SkPixelRef> fPixelRef;
|
sk_sp<SkPixelRef> fPixelRef;
|
||||||
SkPixmap fPixmap;
|
SkPixmap fPixmap;
|
||||||
|
sk_sp<SkMipMap> fMips;
|
||||||
|
|
||||||
|
friend class SkImage_Raster;
|
||||||
friend class SkReadBuffer; // unflatten
|
friend class SkReadBuffer; // unflatten
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class SkData;
|
|||||||
class SkCanvas;
|
class SkCanvas;
|
||||||
class SkImageFilter;
|
class SkImageFilter;
|
||||||
class SkImageGenerator;
|
class SkImageGenerator;
|
||||||
class SkM44;
|
class SkMipMap;
|
||||||
class SkPaint;
|
class SkPaint;
|
||||||
class SkPicture;
|
class SkPicture;
|
||||||
class SkSurface;
|
class SkSurface;
|
||||||
@ -55,6 +55,20 @@ struct SkFilterOptions {
|
|||||||
SkMipmapMode fMipmap;
|
SkMipmapMode fMipmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SkMipmapBuilder {
|
||||||
|
public:
|
||||||
|
SkMipmapBuilder(const SkImageInfo&);
|
||||||
|
~SkMipmapBuilder();
|
||||||
|
|
||||||
|
int countLevels() const;
|
||||||
|
SkPixmap level(int index) const;
|
||||||
|
|
||||||
|
sk_sp<SkMipMap> detach();
|
||||||
|
|
||||||
|
private:
|
||||||
|
sk_sp<SkMipMap> fMM;
|
||||||
|
};
|
||||||
|
|
||||||
/** \class SkImage
|
/** \class SkImage
|
||||||
SkImage describes a two dimensional array of pixels to draw. The pixels may be
|
SkImage describes a two dimensional array of pixels to draw. The pixels may be
|
||||||
decoded in a raster bitmap, encoded in a SkPicture or compressed data stream,
|
decoded in a raster bitmap, encoded in a SkPicture or compressed data stream,
|
||||||
@ -1184,6 +1198,20 @@ public:
|
|||||||
*/
|
*/
|
||||||
sk_sp<SkImage> makeSubset(const SkIRect& subset, GrDirectContext* direct = nullptr) const;
|
sk_sp<SkImage> makeSubset(const SkIRect& subset, GrDirectContext* direct = nullptr) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the image has mipmap levels.
|
||||||
|
*/
|
||||||
|
bool hasMipmaps() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an image with the same "base" pixels as the this image, but with mipmap levels
|
||||||
|
* as well. If this image already has mipmap levels, they will be replaced with new ones.
|
||||||
|
*
|
||||||
|
* If data == nullptr, the mipmap levels are computed automatically.
|
||||||
|
* If data != nullptr, then the caller has provided the data for each level.
|
||||||
|
*/
|
||||||
|
sk_sp<SkImage> withMipmaps(sk_sp<SkMipMap> data) const;
|
||||||
|
|
||||||
/** Returns SkImage backed by GPU texture associated with context. Returned SkImage is
|
/** Returns SkImage backed by GPU texture associated with context. Returned SkImage is
|
||||||
compatible with SkSurface created with dstColorSpace. The returned SkImage respects
|
compatible with SkSurface created with dstColorSpace. The returned SkImage respects
|
||||||
mipMapped setting; if mipMapped equals GrMipMapped::kYes, the backing texture
|
mipMapped setting; if mipMapped equals GrMipMapped::kYes, the backing texture
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#define SkSurface_DEFINED
|
#define SkSurface_DEFINED
|
||||||
|
|
||||||
#include "include/core/SkImage.h"
|
#include "include/core/SkImage.h"
|
||||||
|
#include "include/core/SkPixmap.h"
|
||||||
#include "include/core/SkRefCnt.h"
|
#include "include/core/SkRefCnt.h"
|
||||||
#include "include/core/SkSurfaceProps.h"
|
#include "include/core/SkSurfaceProps.h"
|
||||||
|
|
||||||
@ -70,6 +71,11 @@ public:
|
|||||||
size_t rowBytes,
|
size_t rowBytes,
|
||||||
const SkSurfaceProps* surfaceProps = nullptr);
|
const SkSurfaceProps* surfaceProps = nullptr);
|
||||||
|
|
||||||
|
static sk_sp<SkSurface> MakeRasterDirect(const SkPixmap& pm,
|
||||||
|
const SkSurfaceProps* props = nullptr) {
|
||||||
|
return MakeRasterDirect(pm.info(), pm.writable_addr(), pm.rowBytes(), props);
|
||||||
|
}
|
||||||
|
|
||||||
/** Allocates raster SkSurface. SkCanvas returned by SkSurface draws directly into pixels.
|
/** Allocates raster SkSurface. SkCanvas returned by SkSurface draws directly into pixels.
|
||||||
releaseProc is called with pixels and context when SkSurface is deleted.
|
releaseProc is called with pixels and context when SkSurface is deleted.
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "src/core/SkConvertPixels.h"
|
#include "src/core/SkConvertPixels.h"
|
||||||
#include "src/core/SkMask.h"
|
#include "src/core/SkMask.h"
|
||||||
#include "src/core/SkMaskFilterBase.h"
|
#include "src/core/SkMaskFilterBase.h"
|
||||||
|
#include "src/core/SkMipMap.h"
|
||||||
#include "src/core/SkPixelRefPriv.h"
|
#include "src/core/SkPixelRefPriv.h"
|
||||||
#include "src/core/SkPixmapPriv.h"
|
#include "src/core/SkPixmapPriv.h"
|
||||||
#include "src/core/SkReadBuffer.h"
|
#include "src/core/SkReadBuffer.h"
|
||||||
@ -42,6 +43,7 @@ SkBitmap::SkBitmap() {}
|
|||||||
SkBitmap::SkBitmap(const SkBitmap& src)
|
SkBitmap::SkBitmap(const SkBitmap& src)
|
||||||
: fPixelRef (src.fPixelRef)
|
: fPixelRef (src.fPixelRef)
|
||||||
, fPixmap (src.fPixmap)
|
, fPixmap (src.fPixmap)
|
||||||
|
, fMips (src.fMips)
|
||||||
{
|
{
|
||||||
SkDEBUGCODE(src.validate();)
|
SkDEBUGCODE(src.validate();)
|
||||||
SkDEBUGCODE(this->validate();)
|
SkDEBUGCODE(this->validate();)
|
||||||
@ -50,6 +52,7 @@ SkBitmap::SkBitmap(const SkBitmap& src)
|
|||||||
SkBitmap::SkBitmap(SkBitmap&& other)
|
SkBitmap::SkBitmap(SkBitmap&& other)
|
||||||
: fPixelRef (std::move(other.fPixelRef))
|
: fPixelRef (std::move(other.fPixelRef))
|
||||||
, fPixmap (std::move(other.fPixmap))
|
, fPixmap (std::move(other.fPixmap))
|
||||||
|
, fMips (std::move(other.fMips))
|
||||||
{
|
{
|
||||||
SkASSERT(!other.fPixelRef);
|
SkASSERT(!other.fPixelRef);
|
||||||
other.fPixmap.reset();
|
other.fPixmap.reset();
|
||||||
@ -61,6 +64,7 @@ SkBitmap& SkBitmap::operator=(const SkBitmap& src) {
|
|||||||
if (this != &src) {
|
if (this != &src) {
|
||||||
fPixelRef = src.fPixelRef;
|
fPixelRef = src.fPixelRef;
|
||||||
fPixmap = src.fPixmap;
|
fPixmap = src.fPixmap;
|
||||||
|
fMips = src.fMips;
|
||||||
}
|
}
|
||||||
SkDEBUGCODE(this->validate();)
|
SkDEBUGCODE(this->validate();)
|
||||||
return *this;
|
return *this;
|
||||||
@ -70,6 +74,7 @@ SkBitmap& SkBitmap::operator=(SkBitmap&& other) {
|
|||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
fPixelRef = std::move(other.fPixelRef);
|
fPixelRef = std::move(other.fPixelRef);
|
||||||
fPixmap = std::move(other.fPixmap);
|
fPixmap = std::move(other.fPixmap);
|
||||||
|
fMips = std::move(other.fMips);
|
||||||
SkASSERT(!other.fPixelRef);
|
SkASSERT(!other.fPixelRef);
|
||||||
other.fPixmap.reset();
|
other.fPixmap.reset();
|
||||||
}
|
}
|
||||||
@ -85,6 +90,7 @@ void SkBitmap::swap(SkBitmap& other) {
|
|||||||
void SkBitmap::reset() {
|
void SkBitmap::reset() {
|
||||||
fPixelRef = nullptr; // Free pixels.
|
fPixelRef = nullptr; // Free pixels.
|
||||||
fPixmap.reset();
|
fPixmap.reset();
|
||||||
|
fMips.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkBitmap::getBounds(SkRect* bounds) const {
|
void SkBitmap::getBounds(SkRect* bounds) const {
|
||||||
|
@ -15,6 +15,18 @@
|
|||||||
#include "src/core/SkMipMap.h"
|
#include "src/core/SkMipMap.h"
|
||||||
#include "src/image/SkImage_Base.h"
|
#include "src/image/SkImage_Base.h"
|
||||||
|
|
||||||
|
// Try to load from the base image, or from the cache
|
||||||
|
static sk_sp<const SkMipMap> try_load_mips(const SkImage_Base* image) {
|
||||||
|
sk_sp<const SkMipMap> mips = image->refMips();
|
||||||
|
if (!mips) {
|
||||||
|
mips.reset(SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(image)));
|
||||||
|
}
|
||||||
|
if (!mips) {
|
||||||
|
mips.reset(SkMipMapCache::AddAndRef(image));
|
||||||
|
}
|
||||||
|
return mips;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
SkBitmapController::State* SkBitmapController::RequestBitmap(const SkImage_Base* image,
|
SkBitmapController::State* SkBitmapController::RequestBitmap(const SkImage_Base* image,
|
||||||
@ -60,12 +72,9 @@ bool SkBitmapController::State::processMediumRequest(const SkImage_Base* image)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
|
if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) {
|
||||||
fCurrMip.reset(SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(image)));
|
fCurrMip = try_load_mips(image);
|
||||||
if (nullptr == fCurrMip.get()) {
|
if (!fCurrMip) {
|
||||||
fCurrMip.reset(SkMipMapCache::AddAndRef(image));
|
return false;
|
||||||
if (nullptr == fCurrMip.get()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// diagnostic for a crasher...
|
// diagnostic for a crasher...
|
||||||
SkASSERT_RELEASE(fCurrMip->data());
|
SkASSERT_RELEASE(fCurrMip->data());
|
||||||
@ -143,16 +152,11 @@ SkMipmapAccessor::SkMipmapAccessor(const SkImage_Base* image, const SkMatrix& in
|
|||||||
}
|
}
|
||||||
// load fCurrMip if needed
|
// load fCurrMip if needed
|
||||||
if (levelNum > 0 || (fResolvedMode == SkMipmapMode::kLinear && lowerWeight > 0)) {
|
if (levelNum > 0 || (fResolvedMode == SkMipmapMode::kLinear && lowerWeight > 0)) {
|
||||||
// try to load from the cache
|
fCurrMip = try_load_mips(image);
|
||||||
fCurrMip.reset(SkMipMapCache::FindAndRef(SkBitmapCacheDesc::Make(image)));
|
|
||||||
if (!fCurrMip) {
|
if (!fCurrMip) {
|
||||||
fCurrMip.reset(SkMipMapCache::AddAndRef(image));
|
load_upper_from_base();
|
||||||
if (!fCurrMip) {
|
fResolvedMode = SkMipmapMode::kNone;
|
||||||
load_upper_from_base();
|
} else {
|
||||||
fResolvedMode = SkMipmapMode::kNone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fCurrMip) {
|
|
||||||
SkMipMap::Level levelRec;
|
SkMipMap::Level levelRec;
|
||||||
|
|
||||||
SkASSERT(fResolvedMode != SkMipmapMode::kNone);
|
SkASSERT(fResolvedMode != SkMipmapMode::kNone);
|
||||||
|
@ -395,7 +395,8 @@ size_t SkMipMap::AllocLevelsSize(int levelCount, size_t pixelSize) {
|
|||||||
return SkTo<int32_t>(size);
|
return SkTo<int32_t>(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
|
SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact,
|
||||||
|
bool computeContents) {
|
||||||
typedef void FilterProc(void*, const void* srcPtr, size_t srcRB, int count);
|
typedef void FilterProc(void*, const void* srcPtr, size_t srcRB, int count);
|
||||||
|
|
||||||
FilterProc* proc_1_2 = nullptr;
|
FilterProc* proc_1_2 = nullptr;
|
||||||
@ -632,14 +633,16 @@ SkMipMap* SkMipMap::Build(const SkPixmap& src, SkDiscardableFactoryProc fact) {
|
|||||||
SkIntToScalar(height) / src.height());
|
SkIntToScalar(height) / src.height());
|
||||||
|
|
||||||
const SkPixmap& dstPM = levels[i].fPixmap;
|
const SkPixmap& dstPM = levels[i].fPixmap;
|
||||||
const void* srcBasePtr = srcPM.addr();
|
if (computeContents) {
|
||||||
void* dstBasePtr = dstPM.writable_addr();
|
const void* srcBasePtr = srcPM.addr();
|
||||||
|
void* dstBasePtr = dstPM.writable_addr();
|
||||||
|
|
||||||
const size_t srcRB = srcPM.rowBytes();
|
const size_t srcRB = srcPM.rowBytes();
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
proc(dstBasePtr, srcBasePtr, srcRB, width);
|
proc(dstBasePtr, srcBasePtr, srcRB, width);
|
||||||
srcBasePtr = (char*)srcBasePtr + srcRB * 2; // jump two rows
|
srcBasePtr = (char*)srcBasePtr + srcRB * 2; // jump two rows
|
||||||
dstBasePtr = (char*)dstBasePtr + dstPM.rowBytes();
|
dstBasePtr = (char*)dstBasePtr + dstPM.rowBytes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
srcPM = dstPM;
|
srcPM = dstPM;
|
||||||
addr += height * rowBytes;
|
addr += height * rowBytes;
|
||||||
|
@ -29,7 +29,11 @@ typedef SkDiscardableMemory* (*SkDiscardableFactoryProc)(size_t bytes);
|
|||||||
*/
|
*/
|
||||||
class SkMipMap : public SkCachedData {
|
class SkMipMap : public SkCachedData {
|
||||||
public:
|
public:
|
||||||
static SkMipMap* Build(const SkPixmap& src, SkDiscardableFactoryProc);
|
// Allocate and fill-in a mipmap. If computeContents is false, we just allocated
|
||||||
|
// and compute the sizes/rowbytes, but leave the pixel-data uninitialized.
|
||||||
|
static SkMipMap* Build(const SkPixmap& src, SkDiscardableFactoryProc,
|
||||||
|
bool computeContents = true);
|
||||||
|
|
||||||
static SkMipMap* Build(const SkBitmap& src, SkDiscardableFactoryProc);
|
static SkMipMap* Build(const SkBitmap& src, SkDiscardableFactoryProc);
|
||||||
|
|
||||||
// Determines how many levels a SkMipMap will have without creating that mipmap.
|
// Determines how many levels a SkMipMap will have without creating that mipmap.
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "src/core/SkImageFilterCache.h"
|
#include "src/core/SkImageFilterCache.h"
|
||||||
#include "src/core/SkImageFilter_Base.h"
|
#include "src/core/SkImageFilter_Base.h"
|
||||||
#include "src/core/SkImagePriv.h"
|
#include "src/core/SkImagePriv.h"
|
||||||
|
#include "src/core/SkMipMap.h"
|
||||||
#include "src/core/SkNextID.h"
|
#include "src/core/SkNextID.h"
|
||||||
#include "src/core/SkSpecialImage.h"
|
#include "src/core/SkSpecialImage.h"
|
||||||
#include "src/image/SkImage_Base.h"
|
#include "src/image/SkImage_Base.h"
|
||||||
@ -649,3 +650,41 @@ SkIRect SkImage_getSubset(const SkImage* image) {
|
|||||||
SkASSERT(image);
|
SkASSERT(image);
|
||||||
return as_IB(image)->onGetSubset();
|
return as_IB(image)->onGetSubset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
SkMipmapBuilder::SkMipmapBuilder(const SkImageInfo& info) {
|
||||||
|
fMM = sk_sp<SkMipMap>(SkMipMap::Build({info, nullptr, 0}, nullptr, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
SkMipmapBuilder::~SkMipmapBuilder() {}
|
||||||
|
|
||||||
|
int SkMipmapBuilder::countLevels() const {
|
||||||
|
return fMM ? fMM->countLevels() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkPixmap SkMipmapBuilder::level(int index) const {
|
||||||
|
SkPixmap pm;
|
||||||
|
|
||||||
|
SkMipMap::Level level;
|
||||||
|
if (fMM && fMM->getLevel(index, &level)) {
|
||||||
|
pm = level.fPixmap;
|
||||||
|
}
|
||||||
|
return pm;
|
||||||
|
}
|
||||||
|
|
||||||
|
sk_sp<SkMipMap> SkMipmapBuilder::detach() {
|
||||||
|
return std::move(fMM);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SkImage::hasMipmaps() const {
|
||||||
|
return as_IB(this)->onPeekMips() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
sk_sp<SkImage> SkImage::withMipmaps(sk_sp<SkMipMap> data) const {
|
||||||
|
auto result = as_IB(this)->onMakeWithMipmaps(std::move(data));
|
||||||
|
if (!result) {
|
||||||
|
result = sk_ref_sp((const_cast<SkImage*>(this)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "include/core/SkImage.h"
|
#include "include/core/SkImage.h"
|
||||||
#include "include/core/SkSurface.h"
|
#include "include/core/SkSurface.h"
|
||||||
|
#include "src/core/SkMipMap.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#if SK_SUPPORT_GPU
|
#if SK_SUPPORT_GPU
|
||||||
@ -46,6 +47,12 @@ public:
|
|||||||
virtual bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
|
virtual bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
|
||||||
int srcX, int srcY, CachingHint) const = 0;
|
int srcX, int srcY, CachingHint) const = 0;
|
||||||
|
|
||||||
|
virtual SkMipMap* onPeekMips() const { return nullptr; }
|
||||||
|
|
||||||
|
sk_sp<SkMipMap> refMips() const {
|
||||||
|
return sk_ref_sp(this->onPeekMips());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation does a rescale/read and then calls the callback.
|
* Default implementation does a rescale/read and then calls the callback.
|
||||||
*/
|
*/
|
||||||
@ -128,6 +135,11 @@ public:
|
|||||||
|
|
||||||
virtual sk_sp<SkImage> onReinterpretColorSpace(sk_sp<SkColorSpace>) const = 0;
|
virtual sk_sp<SkImage> onReinterpretColorSpace(sk_sp<SkColorSpace>) const = 0;
|
||||||
|
|
||||||
|
// on failure, returns nullptr
|
||||||
|
virtual sk_sp<SkImage> onMakeWithMipmaps(sk_sp<SkMipMap>) const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SkImage_Base(const SkImageInfo& info, uint32_t uniqueID);
|
SkImage_Base(const SkImageInfo& info, uint32_t uniqueID);
|
||||||
|
|
||||||
|
@ -123,6 +123,18 @@ public:
|
|||||||
void onUnpinAsTexture(GrContext*) const override;
|
void onUnpinAsTexture(GrContext*) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
SkMipMap* onPeekMips() const override { return fBitmap.fMips.get(); }
|
||||||
|
|
||||||
|
sk_sp<SkImage> onMakeWithMipmaps(sk_sp<SkMipMap> mips) const override {
|
||||||
|
auto img = new SkImage_Raster(fBitmap);
|
||||||
|
if (mips) {
|
||||||
|
img->fBitmap.fMips = std::move(mips);
|
||||||
|
} else {
|
||||||
|
img->fBitmap.fMips.reset(SkMipMap::Build(fBitmap.pixmap(), nullptr));
|
||||||
|
}
|
||||||
|
return sk_sp<SkImage>(img);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SkBitmap fBitmap;
|
SkBitmap fBitmap;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "include/utils/SkRandom.h"
|
#include "include/utils/SkRandom.h"
|
||||||
#include "src/core/SkMipMap.h"
|
#include "src/core/SkMipMap.h"
|
||||||
#include "tests/Test.h"
|
#include "tests/Test.h"
|
||||||
|
#include "tools/Resources.h"
|
||||||
|
|
||||||
static void make_bitmap(SkBitmap* bm, int width, int height) {
|
static void make_bitmap(SkBitmap* bm, int width, int height) {
|
||||||
bm->allocN32Pixels(width, height);
|
bm->allocN32Pixels(width, height);
|
||||||
@ -209,3 +210,29 @@ DEF_TEST(MipMap_F16, reporter) {
|
|||||||
bmp.eraseColor(0);
|
bmp.eraseColor(0);
|
||||||
sk_sp<SkMipMap> mipmap(SkMipMap::Build(bmp, nullptr));
|
sk_sp<SkMipMap> mipmap(SkMipMap::Build(bmp, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "include/core/SkCanvas.h"
|
||||||
|
#include "include/core/SkSurface.h"
|
||||||
|
|
||||||
|
DEF_TEST(image_mip_factory, reporter) {
|
||||||
|
// TODO: what do to about lazy images and mipmaps?
|
||||||
|
auto img = GetResourceAsImage("images/mandrill_128.png")->makeRasterImage();
|
||||||
|
|
||||||
|
REPORTER_ASSERT(reporter, !img->hasMipmaps());
|
||||||
|
auto img1 = img->withMipmaps(nullptr);
|
||||||
|
REPORTER_ASSERT(reporter, img.get() != img1.get());
|
||||||
|
REPORTER_ASSERT(reporter, img1->hasMipmaps());
|
||||||
|
|
||||||
|
SkMipmapBuilder builder(img->imageInfo());
|
||||||
|
int count = builder.countLevels();
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
SkPixmap pm = builder.level(i);
|
||||||
|
auto surf = SkSurface::MakeRasterDirect(pm);
|
||||||
|
surf->getCanvas()->drawImageRect(img, SkRect::MakeIWH(pm.width(), pm.height()), nullptr);
|
||||||
|
}
|
||||||
|
auto img2 = img->withMipmaps(builder.detach());
|
||||||
|
REPORTER_ASSERT(reporter, !builder.detach());
|
||||||
|
REPORTER_ASSERT(reporter, img.get() != img2.get());
|
||||||
|
REPORTER_ASSERT(reporter, img1.get() != img2.get());
|
||||||
|
REPORTER_ASSERT(reporter, img2->hasMipmaps());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user