зеркало из https://github.com/mozilla/moz-skia.git
API to support native scaling by image-generator
BUG=skia: Review URL: https://codereview.chromium.org/1396323007
This commit is contained in:
Родитель
dc5685ac37
Коммит
7850eb2f35
149
gm/image.cpp
149
gm/image.cpp
|
@ -240,33 +240,33 @@ static void draw_contents(SkCanvas* canvas) {
|
|||
canvas->drawCircle(50, 50, 35, paint);
|
||||
}
|
||||
|
||||
static SkImage* make_raster(const SkImageInfo& info, GrContext*) {
|
||||
static SkImage* make_raster(const SkImageInfo& info, GrContext*, void (*draw)(SkCanvas*)) {
|
||||
SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
|
||||
draw_contents(surface->getCanvas());
|
||||
draw(surface->getCanvas());
|
||||
return surface->newImageSnapshot();
|
||||
}
|
||||
|
||||
static SkImage* make_picture(const SkImageInfo& info, GrContext*) {
|
||||
static SkImage* make_picture(const SkImageInfo& info, GrContext*, void (*draw)(SkCanvas*)) {
|
||||
SkPictureRecorder recorder;
|
||||
draw_contents(recorder.beginRecording(SkRect::MakeIWH(info.width(), info.height())));
|
||||
draw(recorder.beginRecording(SkRect::MakeIWH(info.width(), info.height())));
|
||||
SkAutoTUnref<SkPicture> pict(recorder.endRecording());
|
||||
return SkImage::NewFromPicture(pict, info.dimensions(), nullptr, nullptr);
|
||||
}
|
||||
|
||||
static SkImage* make_codec(const SkImageInfo& info, GrContext*) {
|
||||
SkAutoTUnref<SkImage> image(make_raster(info, nullptr));
|
||||
static SkImage* make_codec(const SkImageInfo& info, GrContext*, void (*draw)(SkCanvas*)) {
|
||||
SkAutoTUnref<SkImage> image(make_raster(info, nullptr, draw));
|
||||
SkAutoTUnref<SkData> data(image->encode());
|
||||
return SkImage::NewFromEncoded(data);
|
||||
}
|
||||
|
||||
static SkImage* make_gpu(const SkImageInfo& info, GrContext* ctx) {
|
||||
static SkImage* make_gpu(const SkImageInfo& info, GrContext* ctx, void (*draw)(SkCanvas*)) {
|
||||
if (!ctx) { return nullptr; }
|
||||
SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, info));
|
||||
draw_contents(surface->getCanvas());
|
||||
draw(surface->getCanvas());
|
||||
return surface->newImageSnapshot();
|
||||
}
|
||||
|
||||
typedef SkImage* (*ImageMakerProc)(const SkImageInfo&, GrContext*);
|
||||
typedef SkImage* (*ImageMakerProc)(const SkImageInfo&, GrContext*, void (*)(SkCanvas*));
|
||||
|
||||
class ScalePixelsGM : public skiagm::GM {
|
||||
public:
|
||||
|
@ -288,7 +288,7 @@ protected:
|
|||
make_codec, make_raster, make_picture, make_codec, make_gpu,
|
||||
};
|
||||
for (auto& proc : procs) {
|
||||
SkAutoTUnref<SkImage> image(proc(info, canvas->getGrContext()));
|
||||
SkAutoTUnref<SkImage> image(proc(info, canvas->getGrContext(), draw_contents));
|
||||
if (image) {
|
||||
show_scaled_pixels(canvas, image);
|
||||
}
|
||||
|
@ -300,3 +300,132 @@ private:
|
|||
typedef skiagm::GM INHERITED;
|
||||
};
|
||||
DEF_GM( return new ScalePixelsGM; )
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "SkImageGenerator.h"
|
||||
|
||||
static SkImageInfo make_info(SkImage* img) {
|
||||
return SkImageInfo::MakeN32(img->width(), img->height(),
|
||||
img->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
|
||||
}
|
||||
|
||||
// Its simple, but I wonder if we should expose this formally?
|
||||
//
|
||||
class ImageGeneratorFromImage : public SkImageGenerator {
|
||||
public:
|
||||
ImageGeneratorFromImage(SkImage* img) : INHERITED(make_info(img)), fImg(SkRef(img)) {}
|
||||
|
||||
protected:
|
||||
bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[],
|
||||
int* ctableCount) override {
|
||||
return fImg->readPixels(info, pixels, rowBytes, 0, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
SkAutoTUnref<SkImage> fImg;
|
||||
|
||||
typedef SkImageGenerator INHERITED;
|
||||
};
|
||||
|
||||
static void draw_opaque_contents(SkCanvas* canvas) {
|
||||
canvas->drawColor(0xFFFF8844);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(20);
|
||||
canvas->drawCircle(50, 50, 35, paint);
|
||||
}
|
||||
|
||||
static SkImageGenerator* gen_raster(const SkImageInfo& info) {
|
||||
SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
|
||||
draw_opaque_contents(surface->getCanvas());
|
||||
SkAutoTUnref<SkImage> img(surface->newImageSnapshot());
|
||||
return new ImageGeneratorFromImage(img);
|
||||
}
|
||||
|
||||
static SkImageGenerator* gen_picture(const SkImageInfo& info) {
|
||||
SkPictureRecorder recorder;
|
||||
draw_opaque_contents(recorder.beginRecording(SkRect::MakeIWH(info.width(), info.height())));
|
||||
SkAutoTUnref<SkPicture> pict(recorder.endRecording());
|
||||
return SkImageGenerator::NewFromPicture(info.dimensions(), pict, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static SkImageGenerator* gen_png(const SkImageInfo& info) {
|
||||
SkAutoTUnref<SkImage> image(make_raster(info, nullptr, draw_opaque_contents));
|
||||
SkAutoTUnref<SkData> data(image->encode(SkImageEncoder::kPNG_Type, 100));
|
||||
return SkImageGenerator::NewFromEncoded(data);
|
||||
}
|
||||
|
||||
static SkImageGenerator* gen_jpg(const SkImageInfo& info) {
|
||||
SkAutoTUnref<SkImage> image(make_raster(info, nullptr, draw_opaque_contents));
|
||||
SkAutoTUnref<SkData> data(image->encode(SkImageEncoder::kJPEG_Type, 100));
|
||||
return SkImageGenerator::NewFromEncoded(data);
|
||||
}
|
||||
|
||||
typedef SkImageGenerator* (*GeneratorMakerProc)(const SkImageInfo&);
|
||||
|
||||
static void show_scaled_generator(SkCanvas* canvas, SkImageGenerator* gen) {
|
||||
const SkImageInfo genInfo = gen->getInfo();
|
||||
|
||||
SkAutoCanvasRestore acr(canvas, true);
|
||||
|
||||
SkBitmap bm;
|
||||
bm.allocPixels(genInfo);
|
||||
if (gen->getPixels(bm.info(), bm.getPixels(), bm.rowBytes())) {
|
||||
canvas->drawBitmap(bm, 0, 0, nullptr);
|
||||
}
|
||||
canvas->translate(110, 0);
|
||||
|
||||
const float scales[] = { 0.75f, 0.5f, 0.25f };
|
||||
for (auto scale : scales) {
|
||||
SkImageGenerator::SupportedSizes sizes;
|
||||
if (gen->computeScaledDimensions(scale, &sizes)) {
|
||||
const SkImageInfo info = SkImageInfo::MakeN32Premul(sizes.fSizes[0].width(),
|
||||
sizes.fSizes[0].height());
|
||||
bm.allocPixels(info);
|
||||
SkPixmap pmap;
|
||||
bm.peekPixels(&pmap);
|
||||
if (gen->generateScaledPixels(pmap)) {
|
||||
canvas->drawBitmap(bm, 0, SkIntToScalar(genInfo.height() - info.height())/2);
|
||||
}
|
||||
}
|
||||
canvas->translate(100, 0);
|
||||
}
|
||||
}
|
||||
|
||||
class ScaleGeneratorGM : public skiagm::GM {
|
||||
public:
|
||||
ScaleGeneratorGM() {}
|
||||
|
||||
protected:
|
||||
SkString onShortName() override {
|
||||
return SkString("scale-generator");
|
||||
}
|
||||
|
||||
SkISize onISize() override {
|
||||
return SkISize::Make(500, 500);
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
canvas->translate(10, 10);
|
||||
|
||||
// explicitly make it opaque, so we can test JPEG (which is only ever opaque)
|
||||
const SkImageInfo info = SkImageInfo::MakeN32(100, 100, kOpaque_SkAlphaType);
|
||||
|
||||
const GeneratorMakerProc procs[] = {
|
||||
gen_raster, gen_picture, gen_png, gen_jpg,
|
||||
};
|
||||
for (auto& proc : procs) {
|
||||
SkAutoTDelete<SkImageGenerator> gen(proc(info));
|
||||
if (gen) {
|
||||
show_scaled_generator(canvas, gen);
|
||||
}
|
||||
canvas->translate(0, 120);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef skiagm::GM INHERITED;
|
||||
};
|
||||
DEF_GM( return new ScaleGeneratorGM; )
|
||||
|
|
|
@ -152,6 +152,49 @@ public:
|
|||
*/
|
||||
GrTexture* generateTexture(GrContext*, const SkIRect* subset = nullptr);
|
||||
|
||||
struct SupportedSizes {
|
||||
SkISize fSizes[2];
|
||||
};
|
||||
|
||||
/**
|
||||
* Some generators can efficiently scale their contents. If this is supported, the generator
|
||||
* may only support certain scaled dimensions. Call this with the desired scale factor,
|
||||
* and it will return true if scaling is supported, and in supportedSizes[] it will return
|
||||
* the nearest supported dimensions.
|
||||
*
|
||||
* If no native scaling is supported, or scale is invalid (e.g. scale <= 0 || scale > 1)
|
||||
* this will return false, and the supportedsizes will be undefined.
|
||||
*/
|
||||
bool computeScaledDimensions(SkScalar scale, SupportedSizes*);
|
||||
|
||||
/**
|
||||
* Scale the generator's pixels to fit into scaledSize.
|
||||
* This routine also support retrieving only a subset of the pixels. That subset is specified
|
||||
* by the following rectangle (in the scaled space):
|
||||
*
|
||||
* subset = SkIRect::MakeXYWH(subsetOrigin.x(), subsetOrigin.y(),
|
||||
* subsetPixels.width(), subsetPixels.height())
|
||||
*
|
||||
* If subset is not contained inside the scaledSize, this returns false.
|
||||
*
|
||||
* whole = SkIRect::MakeWH(scaledSize.width(), scaledSize.height())
|
||||
* if (!whole.contains(subset)) {
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* If the requested colortype/alphatype in pixels is not supported,
|
||||
* or the requested scaledSize is not supported, or the generator encounters an error,
|
||||
* this returns false.
|
||||
*/
|
||||
bool generateScaledPixels(const SkISize& scaledSize, const SkIPoint& subsetOrigin,
|
||||
const SkPixmap& subsetPixels);
|
||||
|
||||
bool generateScaledPixels(const SkPixmap& scaledPixels) {
|
||||
return this->generateScaledPixels(SkISize::Make(scaledPixels.width(),
|
||||
scaledPixels.height()),
|
||||
SkIPoint::Make(0, 0), scaledPixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the default image decoder system can interpret the specified (encoded) data, then
|
||||
* this returns a new ImageGenerator for it. Otherwise this returns NULL. Either way
|
||||
|
@ -199,6 +242,13 @@ protected:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool onComputeScaledDimensions(SkScalar, SupportedSizes*) {
|
||||
return false;
|
||||
}
|
||||
virtual bool onGenerateScaledPixels(const SkISize&, const SkIPoint&, const SkPixmap&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tryGenerateBitmap(SkBitmap* bm, const SkImageInfo* optionalInfo, SkBitmap::Allocator*);
|
||||
|
||||
private:
|
||||
|
|
|
@ -111,6 +111,30 @@ GrTexture* SkImageGenerator::generateTexture(GrContext* ctx, const SkIRect* subs
|
|||
return this->onGenerateTexture(ctx, subset);
|
||||
}
|
||||
|
||||
bool SkImageGenerator::computeScaledDimensions(SkScalar scale, SupportedSizes* sizes) {
|
||||
if (scale > 0 && scale <= 1) {
|
||||
return this->onComputeScaledDimensions(scale, sizes);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkImageGenerator::generateScaledPixels(const SkISize& scaledSize,
|
||||
const SkIPoint& subsetOrigin,
|
||||
const SkPixmap& subsetPixels) {
|
||||
if (scaledSize.width() <= 0 || scaledSize.height() <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (subsetPixels.width() <= 0 || subsetPixels.height() <= 0) {
|
||||
return false;
|
||||
}
|
||||
const SkIRect subset = SkIRect::MakeXYWH(subsetOrigin.x(), subsetOrigin.y(),
|
||||
subsetPixels.width(), subsetPixels.height());
|
||||
if (!SkIRect::MakeWH(scaledSize.width(), scaledSize.height()).contains(subset)) {
|
||||
return false;
|
||||
}
|
||||
return this->onGenerateScaledPixels(scaledSize, subsetOrigin, subsetPixels);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkData* SkImageGenerator::onRefEncodedData() {
|
||||
|
|
|
@ -21,6 +21,9 @@ public:
|
|||
protected:
|
||||
bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[],
|
||||
int* ctableCount) override;
|
||||
bool onComputeScaledDimensions(SkScalar scale, SupportedSizes*) override;
|
||||
bool onGenerateScaledPixels(const SkISize&, const SkIPoint&, const SkPixmap&) override;
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
GrTexture* onGenerateTexture(GrContext*, const SkIRect*) override;
|
||||
#endif
|
||||
|
@ -78,6 +81,47 @@ bool SkPictureImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SkPictureImageGenerator::onComputeScaledDimensions(SkScalar scale,
|
||||
SupportedSizes* sizes) {
|
||||
SkASSERT(scale > 0 && scale <= 1);
|
||||
const int w = this->getInfo().width();
|
||||
const int h = this->getInfo().height();
|
||||
const int sw = SkScalarRoundToInt(scale * w);
|
||||
const int sh = SkScalarRoundToInt(scale * h);
|
||||
if (sw > 0 && sh > 0) {
|
||||
sizes->fSizes[0].set(sw, sh);
|
||||
sizes->fSizes[1].set(sw, sh);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkPictureImageGenerator::onGenerateScaledPixels(const SkISize& scaledSize,
|
||||
const SkIPoint& scaledOrigin,
|
||||
const SkPixmap& scaledPixels) {
|
||||
int w = scaledSize.width();
|
||||
int h = scaledSize.height();
|
||||
|
||||
const SkScalar scaleX = SkIntToScalar(w) / this->getInfo().width();
|
||||
const SkScalar scaleY = SkIntToScalar(h) / this->getInfo().height();
|
||||
SkMatrix matrix = SkMatrix::MakeScale(scaleX, scaleY);
|
||||
matrix.postTranslate(-SkIntToScalar(scaledOrigin.x()), -SkIntToScalar(scaledOrigin.y()));
|
||||
|
||||
SkBitmap bitmap;
|
||||
if (!bitmap.installPixels(scaledPixels.info(), scaledPixels.writable_addr(),
|
||||
scaledPixels.rowBytes())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bitmap.eraseColor(SK_ColorTRANSPARENT);
|
||||
SkCanvas canvas(bitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
|
||||
matrix.preConcat(fMatrix);
|
||||
canvas.drawPicture(fPicture, &matrix, fPaint.getMaybeNull());
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkImageGenerator* SkImageGenerator::NewFromPicture(const SkISize& size, const SkPicture* picture,
|
||||
const SkMatrix* matrix, const SkPaint* paint) {
|
||||
return SkPictureImageGenerator::Create(size, picture, matrix, paint);
|
||||
|
|
Загрузка…
Ссылка в новой задаче