API to support native scaling by image-generator

BUG=skia:

Review URL: https://codereview.chromium.org/1396323007
This commit is contained in:
reed 2015-12-02 14:19:47 -08:00 коммит произвёл Commit bot
Родитель dc5685ac37
Коммит 7850eb2f35
4 изменённых файлов: 257 добавлений и 10 удалений

Просмотреть файл

@ -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);