Pass sampling to drawimage

Change-Id: Ia1cd20bb4ea1afeb31a1e47da054c309bdaf15bd
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/358216
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
Mike Reed 2021-01-23 21:14:47 -05:00 committed by Skia Commit-Bot
parent 8d29ab6309
commit d396cd50ff
12 changed files with 97 additions and 80 deletions

View File

@ -370,17 +370,20 @@ DEF_SIMPLE_GM(bleed_downscale, canvas, 360, 240) {
const SkCanvas::SrcRectConstraint constraints[] = {
SkCanvas::kStrict_SrcRectConstraint, SkCanvas::kFast_SrcRectConstraint
};
const SkFilterQuality qualities[] = {
kNone_SkFilterQuality, kLow_SkFilterQuality, kMedium_SkFilterQuality
const SkSamplingOptions samplings[] = {
SkSamplingOptions(SkFilterMode::kNearest),
SkSamplingOptions(SkFilterMode::kLinear),
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear),
};
for (auto constraint : constraints) {
canvas->save();
for (auto quality : qualities) {
paint.setFilterQuality(quality);
for (auto sampling : samplings) {
auto surf = ToolUtils::makeSurface(canvas, SkImageInfo::MakeN32Premul(1, 1));
surf->getCanvas()->drawImageRect(img, src, SkRect::MakeWH(1, 1), &paint, constraint);
surf->getCanvas()->drawImageRect(img, src, SkRect::MakeWH(1, 1), sampling,
nullptr, constraint);
// now blow up the 1 pixel result
canvas->drawImageRect(surf->makeImageSnapshot(), SkRect::MakeWH(100, 100), nullptr);
canvas->drawImageRect(surf->makeImageSnapshot(), SkRect::MakeWH(100, 100),
SkSamplingOptions());
canvas->translate(120, 0);
}
canvas->restore();

View File

@ -306,7 +306,7 @@ protected:
break;
}
auto pad = PadForSigma(sigma);
canvas->drawImage(img, -pad, -pad, &paint);
canvas->drawImage(img, -pad, -pad, SkSamplingOptions(), &paint);
#if 0 // Uncomment to hairline stroke around blurred rect in red on top of the blur result.
// The rect is defined at integer coords. We inset by 1/2 pixel so our stroke lies on top
// of the edge pixels.
@ -470,9 +470,9 @@ private:
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
paint.setColorFilter(std::move(greenifyCF));
surf->getCanvas()->drawImage(a, 0, 0, &paint);
surf->getCanvas()->drawImage(a, 0, 0, SkSamplingOptions(), &paint);
paint.setBlendMode(SkBlendMode::kDifference);
surf->getCanvas()->drawImage(r, 0, 0, &paint);
surf->getCanvas()->drawImage(r, 0, 0, SkSamplingOptions(), &paint);
d = surf->makeImageSnapshot();
}
}

View File

@ -270,8 +270,7 @@ private:
int numMipLevels = SkMipmap::ComputeLevelCount(levelDimensions.width(),
levelDimensions.height()) + 1;
SkPaint imagePaint;
imagePaint.setFilterQuality(kHigh_SkFilterQuality); // to force mipmapping
SkSamplingOptions sampling({1.0f/3, 1.0f/3});
bool isCompressed = false;
if (image->isTextureBacked()) {
@ -289,7 +288,7 @@ private:
SkRect r = SkRect::MakeXYWH(offset.fX, offset.fY,
levelDimensions.width(), levelDimensions.height());
canvas->drawImageRect(image, r, &imagePaint);
canvas->drawImageRect(image, r, sampling);
if (!isCompressed) {
// Make it obvious which drawImages used decompressed images
canvas->drawRect(r, redStrokePaint);

View File

@ -44,6 +44,7 @@ static SkBitmap make_chessbm(int w, int h) {
p[x] = ((x + y) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
}
}
bm.setImmutable();
return bm;
}
@ -105,43 +106,48 @@ static sk_sp<SkImage> makebm(SkCanvas* origCanvas, SkBitmap* resultBM, int w, in
}
static void bitmapproc(SkCanvas* canvas, SkImage*, const SkBitmap& bm, const SkIRect& srcR,
const SkRect& dstR, const SkPaint* paint) {
canvas->drawBitmapRect(bm, srcR, dstR, paint);
const SkRect& dstR, const SkSamplingOptions& sampling,
const SkPaint* paint) {
canvas->drawImageRect(bm.asImage(), SkRect::Make(srcR), dstR, sampling, paint,
SkCanvas::kStrict_SrcRectConstraint);
}
static void bitmapsubsetproc(SkCanvas* canvas, SkImage*, const SkBitmap& bm, const SkIRect& srcR,
const SkRect& dstR, const SkPaint* paint) {
const SkRect& dstR, const SkSamplingOptions& sampling,
const SkPaint* paint) {
if (!bm.bounds().contains(srcR)) {
bitmapproc(canvas, nullptr, bm, srcR, dstR, paint);
bitmapproc(canvas, nullptr, bm, srcR, dstR, sampling, paint);
return;
}
SkBitmap subset;
if (bm.extractSubset(&subset, srcR)) {
canvas->drawBitmapRect(subset, dstR, paint);
canvas->drawImageRect(subset.asImage(), dstR, sampling, paint);
}
}
static void imageproc(SkCanvas* canvas, SkImage* image, const SkBitmap&, const SkIRect& srcR,
const SkRect& dstR, const SkPaint* paint) {
canvas->drawImageRect(image, srcR, dstR, paint);
const SkRect& dstR, const SkSamplingOptions& sampling, const SkPaint* paint) {
canvas->drawImageRect(image, SkRect::Make(srcR), dstR, sampling, paint,
SkCanvas::kStrict_SrcRectConstraint);
}
static void imagesubsetproc(SkCanvas* canvas, SkImage* image, const SkBitmap& bm,
const SkIRect& srcR, const SkRect& dstR, const SkPaint* paint) {
const SkIRect& srcR, const SkRect& dstR,
const SkSamplingOptions& sampling, const SkPaint* paint) {
if (!image->bounds().contains(srcR)) {
imageproc(canvas, image, bm, srcR, dstR, paint);
imageproc(canvas, image, bm, srcR, dstR, sampling, paint);
return;
}
auto direct = GrAsDirectContext(canvas->recordingContext());
if (sk_sp<SkImage> subset = image->makeSubset(srcR, direct)) {
canvas->drawImageRect(subset, dstR, paint);
canvas->drawImageRect(subset, dstR, sampling, paint);
}
}
typedef void DrawRectRectProc(SkCanvas*, SkImage*, const SkBitmap&, const SkIRect&, const SkRect&,
const SkPaint*);
const SkSamplingOptions&, const SkPaint*);
constexpr int gSize = 1024;
constexpr int gBmpSize = 2048;
@ -181,7 +187,7 @@ protected:
const int kPadY = 40;
SkPaint paint;
paint.setAlphaf(0.125f);
canvas->drawImageRect(fImage, SkRect::MakeIWH(gSize, gSize), &paint);
canvas->drawImageRect(fImage, SkRect::MakeIWH(gSize, gSize), SkSamplingOptions(), &paint);
canvas->translate(SK_Scalar1 * kPadX / 2,
SK_Scalar1 * kPadY / 2);
SkPaint blackPaint;
@ -202,7 +208,8 @@ protected:
for (int h = 1; h <= kMaxSrcRectSize; h *= 4) {
SkIRect srcRect = SkIRect::MakeXYWH((gBmpSize - w) / 2, (gBmpSize - h) / 2, w, h);
fProc(canvas, fImage.get(), fLargeBitmap, srcRect, dstRect, nullptr);
fProc(canvas, fImage.get(), fLargeBitmap, srcRect, dstRect, SkSamplingOptions(),
nullptr);
SkString label;
label.appendf("%d x %d", w, h);
@ -232,17 +239,15 @@ protected:
// SkGpuDevice::drawPath() -> SkGpuDevice::drawWithMaskFilter()
SkIRect srcRect;
SkPaint paint;
SkBitmap bm;
bm = make_chessbm(5, 5);
paint.setFilterQuality(kLow_SkFilterQuality);
SkBitmap bm = make_chessbm(5, 5);
srcRect.setXYWH(1, 1, 3, 3);
paint.setMaskFilter(SkMaskFilter::MakeBlur(
kNormal_SkBlurStyle,
SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5))));
fProc(canvas, bm.asImage().get(), bm, srcRect, dstRect, &paint);
fProc(canvas, bm.asImage().get(), bm, srcRect, dstRect,
SkSamplingOptions(SkFilterMode::kLinear), &paint);
}
}

View File

@ -52,18 +52,18 @@ DEF_SIMPLE_GM_CAN_FAIL(ducky_yuv_blend, canvas, errorMsg, 560, 1130) {
canvas->save();
rowCnt = 0;
};
SkSamplingOptions sampling(SkFilterMode::kLinear,
SkMipmapMode::kNearest);
ToolUtils::draw_checkerboard(
canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, (kDstRect.height() + kPad)/5);
for (auto& fg : duckyFG) {
for (int bm = static_cast<int>(SkBlendMode::kLastCoeffMode) + 1;
bm < static_cast<int>(SkBlendMode::kLastMode);
++bm) {
auto mode = static_cast<SkBlendMode>(bm);
canvas->drawImageRect(duckyBG, kDstRect, sampling, nullptr);
SkPaint paint;
paint.setFilterQuality(kMedium_SkFilterQuality);
canvas->drawImageRect(duckyBG, kDstRect, &paint);
paint.setBlendMode(mode);
canvas->drawImageRect(fg, kDstRect, &paint);
paint.setBlendMode(static_cast<SkBlendMode>(bm));
canvas->drawImageRect(fg, kDstRect, sampling, &paint);
canvas->translate(kDstRect.width() + kPad, 0);
if (++rowCnt == kNumPerRow) {
newRow();

View File

@ -200,7 +200,8 @@ private:
canvas->drawImageRect(image, kSubsets[matIndex],
drawScaled ? SkRect::MakeWH(kImageSize, kImageSize)
: kSubsets[matIndex],
nullptr, SkCanvas::kFast_SrcRectConstraint);
SkSamplingOptions(), nullptr,
SkCanvas::kFast_SrcRectConstraint);
} else {
canvas->drawImage(image, 0, 0);
}

View File

@ -79,10 +79,11 @@ static void test_surface(SkCanvas* canvas, SkSurface* surf, bool usePaint) {
drawContents(surf, SK_ColorBLUE);
SkSamplingOptions sampling;
SkPaint paint;
canvas->drawImage(imgR, 0, 0, usePaint ? &paint : nullptr);
canvas->drawImage(imgG, 0, 80, usePaint ? &paint : nullptr);
canvas->drawImage(imgR, 0, 0, sampling, usePaint ? &paint : nullptr);
canvas->drawImage(imgG, 0, 80, sampling, usePaint ? &paint : nullptr);
surf->draw(canvas, 0, 160, SkSamplingOptions(), usePaint ? &paint : nullptr);
SkRect src1, src2, src3;
@ -97,10 +98,13 @@ static void test_surface(SkCanvas* canvas, SkSurface* surf, bool usePaint) {
dst3.setLTRB(0, 400, 65, 465);
dst4.setLTRB(0, 480, 65, 545);
canvas->drawImageRect(imgR, src1, dst1, usePaint ? &paint : nullptr);
canvas->drawImageRect(imgG, src2, dst2, usePaint ? &paint : nullptr);
canvas->drawImageRect(imgR, src3, dst3, usePaint ? &paint : nullptr);
canvas->drawImageRect(imgG, dst4, usePaint ? &paint : nullptr);
canvas->drawImageRect(imgR, src1, dst1, sampling, usePaint ? &paint : nullptr,
SkCanvas::kStrict_SrcRectConstraint);
canvas->drawImageRect(imgG, src2, dst2, sampling, usePaint ? &paint : nullptr,
SkCanvas::kStrict_SrcRectConstraint);
canvas->drawImageRect(imgR, src3, dst3, sampling, usePaint ? &paint : nullptr,
SkCanvas::kStrict_SrcRectConstraint);
canvas->drawImageRect(imgG, dst4, sampling, usePaint ? &paint : nullptr);
}
class ImageGM : public skiagm::GM {
@ -185,7 +189,7 @@ static void draw_pixmap(SkCanvas* canvas, const SkPixmap& pmap) {
static void show_scaled_pixels(SkCanvas* canvas, SkImage* image) {
SkAutoCanvasRestore acr(canvas, true);
canvas->drawImage(image, 0, 0, nullptr);
canvas->drawImage(image, 0, 0);
canvas->translate(110, 10);
const SkImageInfo info = SkImageInfo::MakeN32Premul(40, 40);
@ -377,7 +381,7 @@ DEF_SIMPLE_GPU_GM(new_texture_image, context, rtc, canvas, 280, 60) {
}
static void draw_pixmap(SkCanvas* canvas, const SkPixmap& pm, SkScalar x, SkScalar y) {
canvas->drawImage(SkImage::MakeRasterCopy(pm), x, y, nullptr);
canvas->drawImage(SkImage::MakeRasterCopy(pm), x, y);
}
static void slam_ff(const SkPixmap& pm) {
@ -451,7 +455,7 @@ DEF_SIMPLE_GM_CAN_FAIL(image_subset, canvas, errorMsg, 440, 220) {
return skiagm::DrawResult::kFail;
}
canvas->drawImage(img, 10, 10, nullptr);
canvas->drawImage(img, 10, 10);
auto sub = img->makeSubset({100, 100, 200, 200});
canvas->drawImage(sub, 220, 10);
sub = serial_deserial(sub.get());

View File

@ -115,7 +115,7 @@ DEF_SIMPLE_GM(fast_slow_blurimagefilter, canvas, 620, 260) {
for (SkScalar outset = 0; outset <= 1; ++outset) {
canvas->save();
canvas->clipRect(r.makeOutset(outset, outset));
canvas->drawImage(image, 0, 0, &paint);
canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
canvas->restore();
canvas->translate(0, r.height() + 20);
}
@ -177,8 +177,8 @@ protected:
{ 0.125f, 0.125f, 530, 420 },
};
SkPaint paint;
paint.setFilterQuality(kMedium_SkFilterQuality);
SkSamplingOptions sampling(SkFilterMode::kLinear,
SkMipmapMode::kLinear);
sk_sp<SkImage> image(GetResourceAsImage("images/mandrill_512.png"));
canvas->translate(20, 20);
@ -186,7 +186,7 @@ protected:
canvas->save();
canvas->translate(xform.fTx, xform.fTy);
canvas->scale(xform.fSx, xform.fSy);
canvas->drawImage(image, 0, 0, &paint);
canvas->drawImage(image, 0, 0, sampling, nullptr);
draw_set(canvas, filters, SK_ARRAY_COUNT(filters));
canvas->restore();
}
@ -235,13 +235,13 @@ DEF_SIMPLE_GM(imagefilters_effect_order, canvas, 512, 512) {
SkRect crop = SkRect::Make(image->bounds());
canvas->save();
canvas->clipRect(crop);
canvas->drawImage(image, 0, 0, &expectedCFPaint); // Filter applied by draw's SkPaint
canvas->drawImage(image, 0, 0, SkSamplingOptions(), &expectedCFPaint); // Filter applied by draw's SkPaint
canvas->restore();
canvas->save();
canvas->translate(image->width(), 0);
canvas->clipRect(crop);
canvas->drawImage(image, 0, 0, &testCFPaint);
canvas->drawImage(image, 0, 0, SkSamplingOptions(), &testCFPaint);
canvas->restore();
// Now test mask filters. These should be run before the image filter, and thus have the same
@ -272,12 +272,12 @@ DEF_SIMPLE_GM(imagefilters_effect_order, canvas, 512, 512) {
canvas->save();
canvas->translate(0, image->height());
canvas->clipRect(crop);
canvas->drawImage(image, 0, 0, &expectedMaskPaint);
canvas->drawImage(image, 0, 0, SkSamplingOptions(), &expectedMaskPaint);
canvas->restore();
canvas->save();
canvas->translate(image->width(), image->height());
canvas->clipRect(crop);
canvas->drawImage(image, 0, 0, &testMaskPaint);
canvas->drawImage(image, 0, 0, SkSamplingOptions(), &testMaskPaint);
canvas->restore();
}

View File

@ -195,30 +195,29 @@ protected:
}
void onDraw(GrRecordingContext*, GrSurfaceDrawContext*, SkCanvas* canvas) override {
auto draw_image = [canvas](SkImage* image, SkFilterQuality fq) -> SkSize {
auto draw_image = [canvas](SkImage* image, const SkSamplingOptions& sampling) -> SkSize {
if (!image) {
return {0, 0};
}
SkPaint paint;
paint.setFilterQuality(fq);
canvas->drawImage(image, 0, 0, &paint);
canvas->drawImage(image, 0, 0, sampling, nullptr);
return {SkIntToScalar(image->width()), SkIntToScalar(image->height())};
};
auto draw_image_rect = [canvas](SkImage* image, SkFilterQuality fq) -> SkSize {
auto draw_image_rect = [canvas](SkImage* image,
const SkSamplingOptions& sampling) -> SkSize {
if (!image) {
return {0, 0};
}
SkPaint paint;
paint.setFilterQuality(fq);
auto subset = SkRect::Make(image->dimensions());
subset.inset(subset.width() * .05f, subset.height() * .1f);
auto dst = SkRect::MakeWH(subset.width(), subset.height());
canvas->drawImageRect(image, subset, dst, &paint);
canvas->drawImageRect(image, subset, dst, sampling, nullptr,
SkCanvas::kStrict_SrcRectConstraint);
return {dst.width(), dst.height()};
};
auto draw_image_shader = [canvas](SkImage* image, SkFilterQuality fq) -> SkSize {
auto draw_image_shader = [canvas](SkImage* image,
const SkSamplingOptions& sampling) -> SkSize {
if (!image) {
return {0, 0};
}
@ -226,7 +225,7 @@ protected:
m.setRotate(45, image->width()/2.f, image->height()/2.f);
SkPaint paint;
paint.setShader(image->makeShader(SkTileMode::kMirror, SkTileMode::kDecal,
SkSamplingOptions(fq), m));
sampling, m));
auto rect = SkRect::MakeWH(image->width() * 1.3f, image->height());
canvas->drawRect(rect, paint);
return {rect.width(), rect.height()};
@ -234,22 +233,26 @@ protected:
canvas->translate(kPad, kPad);
int imageIndex = 0;
using DrawSig = SkSize(SkImage* image, SkFilterQuality fq);
using DrawSig = SkSize(SkImage* image, const SkSamplingOptions&);
using DF = std::function<DrawSig>;
for (const auto& draw : {DF(draw_image), DF(draw_image_rect), DF(draw_image_shader)}) {
for (auto scale : {1.f, 4.f, 0.75f}) {
SkScalar h = 0;
canvas->save();
for (auto fq : {kNone_SkFilterQuality, kLow_SkFilterQuality,
kMedium_SkFilterQuality, kHigh_SkFilterQuality}) {
for (const auto& sampling : {
SkSamplingOptions(SkFilterMode::kNearest),
SkSamplingOptions(SkFilterMode::kLinear),
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest),
SkSamplingOptions({1.0f/3, 1.0f/3})})
{
canvas->save();
canvas->scale(scale, scale);
auto s1 = draw(this->getYUVAImage(imageIndex++), fq);
auto s1 = draw(this->getYUVAImage(imageIndex++), sampling);
canvas->restore();
canvas->translate(kPad + SkScalarCeilToScalar(scale*s1.width()), 0);
canvas->save();
canvas->scale(scale, scale);
auto s2 = draw(fReferenceImage.get(), fq);
auto s2 = draw(fReferenceImage.get(), sampling);
canvas->restore();
canvas->translate(kPad + SkScalarCeilToScalar(scale*s2.width()), 0);
h = std::max({h, s1.height(), s2.height()});

View File

@ -82,10 +82,12 @@ DEF_SIMPLE_GM(imagemasksubset, canvas, 480, 480) {
for (size_t i = 0; i < SK_ARRAY_COUNT(makers); ++i) {
sk_sp<SkImage> image = makers[i](canvas, info);
if (image) {
canvas->drawImageRect(image, SkRect::Make(kSubset), kDest, &paint);
canvas->drawImageRect(image, SkRect::Make(kSubset), kDest, SkSamplingOptions(),
&paint, SkCanvas::kStrict_SrcRectConstraint);
auto direct = GrAsDirectContext(canvas->recordingContext());
sk_sp<SkImage> subset = image->makeSubset(kSubset, direct);
canvas->drawImageRect(subset, kDest.makeOffset(kSize.width() * 1.5f, 0), &paint);
canvas->drawImageRect(subset, kDest.makeOffset(kSize.width() * 1.5f, 0),
SkSamplingOptions(), &paint);
}
canvas->translate(0, kSize.height() * 1.5f);
}

View File

@ -79,7 +79,7 @@ private:
canvas->save();
canvas->clipRect(clipRect);
canvas->scale(scale, scale);
canvas->drawImage(image, 0, 0, &paint);
canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
canvas->restore();
// Draw a boundary rect around the intersection of the clip rect and crop rect.

View File

@ -921,9 +921,9 @@ protected:
constraint = SkCanvas::kStrict_SrcRectConstraint;
}
SkSamplingOptions sampling(SkFilterMode::kLinear);
for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
SkPaint paint;
paint.setFilterQuality(kLow_SkFilterQuality);
if (kIdentity_SkYUVColorSpace == cs) {
// The identity color space needs post processing to appear correctly
paint.setColorFilter(yuv_to_rgb_colorfilter());
@ -945,10 +945,11 @@ protected:
// operate on the YUV components rather than the RGB components.
sk_sp<SkImage> csImage =
fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace, direct);
canvas->drawImageRect(csImage, srcRect, dstRect, &paint, constraint);
canvas->drawImageRect(csImage, srcRect, dstRect, sampling,
&paint, constraint);
} else {
canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect,
&paint, constraint);
sampling, &paint, constraint);
}
dstRect.offset(0.f, cellHeight + kPad);
}
@ -1155,10 +1156,9 @@ static void draw_into_alpha(const SkImage* img, sk_sp<SkColorFilter> cf, const S
auto canvas = SkCanvas::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes());
canvas->scale(1.0f * dst.width() / img->width(), 1.0f * dst.height() / img->height());
SkPaint paint;
paint.setFilterQuality(kLow_SkFilterQuality);
paint.setColorFilter(cf);
paint.setBlendMode(SkBlendMode::kSrc);
canvas->drawImage(img, 0, 0, &paint);
canvas->drawImage(img, 0, 0, SkSamplingOptions(SkFilterMode::kLinear), &paint);
}
static void split_into_yuv(const SkImage* img, SkYUVColorSpace cs, const SkPixmap dst[3]) {
@ -1245,7 +1245,7 @@ protected:
/* limit to max tex size */ false,
/* color space */ nullptr);
if (img) {
canvas->drawImage(img, 0, 0, nullptr);
canvas->drawImage(img, 0, 0);
draw_diff(canvas, 0, fOrig->height(), fOrig.get(), img.get());
}
canvas->translate(fOrig->width(), 0);
@ -1253,10 +1253,10 @@ protected:
canvas->restore();
canvas->translate(-fOrig->width(), 0);
canvas->drawImage(SkImage::MakeRasterCopy(fPM[0]), 0, 0, nullptr);
canvas->drawImage(SkImage::MakeRasterCopy(fPM[1]), 0, fPM[0].height(), nullptr);
canvas->drawImage(SkImage::MakeRasterCopy(fPM[0]), 0, 0);
canvas->drawImage(SkImage::MakeRasterCopy(fPM[1]), 0, fPM[0].height());
canvas->drawImage(SkImage::MakeRasterCopy(fPM[2]),
0, fPM[0].height() + fPM[1].height(), nullptr);
0, fPM[0].height() + fPM[1].height());
}
private: