Example #1
0
void SkScalerContext::getMetrics(SkGlyph* glyph) {
    this->getGlyphContext(*glyph)->generateMetrics(glyph);

    // for now we have separate cache entries for devkerning on and off
    // in the future we might share caches, but make our measure/draw
    // code make the distinction. Thus we zap the values if the caller
    // has not asked for them.
    if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) {
        // no devkern, so zap the fields
        glyph->fLsbDelta = glyph->fRsbDelta = 0;
    }

    // if either dimension is empty, zap the image bounds of the glyph
    if (0 == glyph->fWidth || 0 == glyph->fHeight) {
        glyph->fWidth   = 0;
        glyph->fHeight  = 0;
        glyph->fTop     = 0;
        glyph->fLeft    = 0;
        glyph->fMaskFormat = 0;
        return;
    }

    if (fGenerateImageFromPath) {
        SkPath      devPath, fillPath;
        SkMatrix    fillToDevMatrix;

        this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);

        if (fRasterizer) {
            SkMask  mask;

            if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
                                       fMaskFilter, &mask,
                                       SkMask::kJustComputeBounds_CreateMode)) {
                glyph->fLeft    = mask.fBounds.fLeft;
                glyph->fTop     = mask.fBounds.fTop;
                glyph->fWidth   = SkToU16(mask.fBounds.width());
                glyph->fHeight  = SkToU16(mask.fBounds.height());
            } else {
                goto SK_ERROR;
            }
        } else {
            // just use devPath
            SkIRect ir;
            devPath.getBounds().roundOut(&ir);

            if (ir.isEmpty() || !ir.is16Bit()) {
                goto SK_ERROR;
            }
            glyph->fLeft    = ir.fLeft;
            glyph->fTop     = ir.fTop;
            glyph->fWidth   = SkToU16(ir.width());
            glyph->fHeight  = SkToU16(ir.height());
        }
    }

	if (SkMask::kARGB32_Format != glyph->fMaskFormat) {
		glyph->fMaskFormat = fRec.fMaskFormat;
	}

    if (fMaskFilter) {
        SkMask      src, dst;
        SkMatrix    matrix;

        glyph->toMask(&src);
        fRec.getMatrixFrom2x2(&matrix);

        src.fImage = NULL;  // only want the bounds from the filter
        if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) {
            SkASSERT(dst.fImage == NULL);
            glyph->fLeft    = dst.fBounds.fLeft;
            glyph->fTop     = dst.fBounds.fTop;
            glyph->fWidth   = SkToU16(dst.fBounds.width());
            glyph->fHeight  = SkToU16(dst.fBounds.height());
            glyph->fMaskFormat = dst.fFormat;
        }
    }
    return;

SK_ERROR:
    // draw nothing 'cause we failed
    glyph->fLeft    = 0;
    glyph->fTop     = 0;
    glyph->fWidth   = 0;
    glyph->fHeight  = 0;
    // put a valid value here, in case it was earlier set to
    // MASK_FORMAT_JUST_ADVANCE
    glyph->fMaskFormat = fRec.fMaskFormat;
}
Example #2
0
static void check(skiatest::Reporter* r,
                  const char path[],
                  SkISize size,
                  bool supportsScanlineDecoding,
                  bool supportsSubsetDecoding,
                  bool supports565 = true) {
    SkAutoTDelete<SkStream> stream(resource(path));
    if (!stream) {
        SkDebugf("Missing resource '%s'\n", path);
        return;
    }
    SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach()));
    if (!codec) {
        ERRORF(r, "Unable to decode '%s'", path);
        return;
    }

    // This test is used primarily to verify rewinding works properly.  Using kN32 allows
    // us to test this without the added overhead of creating different bitmaps depending
    // on the color type (ex: building a color table for kIndex8).  DM is where we test
    // decodes to all possible destination color types.
    SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
    REPORTER_ASSERT(r, info.dimensions() == size);

    {
        // Test decoding to 565
        SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType);
        SkCodec::Result expected = (supports565 && info.alphaType() == kOpaque_SkAlphaType) ?
                SkCodec::kSuccess : SkCodec::kInvalidConversion;
        test_info(r, codec, info565, expected, NULL);
    }

    SkBitmap bm;
    bm.allocPixels(info);
    SkAutoLockPixels autoLockPixels(bm);
    SkCodec::Result result =
        codec->getPixels(info, bm.getPixels(), bm.rowBytes(), NULL, NULL, NULL);
    REPORTER_ASSERT(r, result == SkCodec::kSuccess);

    SkMD5::Digest digest;
    md5(bm, &digest);

    // verify that re-decoding gives the same result.
    test_info(r, codec, info, SkCodec::kSuccess, &digest);

    {
        // Check alpha type conversions
        if (info.alphaType() == kOpaque_SkAlphaType) {
            test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType),
                      SkCodec::kInvalidConversion, NULL);
            test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType),
                      SkCodec::kInvalidConversion, NULL);
        } else {
            // Decoding to opaque should fail
            test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType),
                      SkCodec::kInvalidConversion, NULL);
            SkAlphaType otherAt = info.alphaType();
            if (kPremul_SkAlphaType == otherAt) {
                otherAt = kUnpremul_SkAlphaType;
            } else {
                otherAt = kPremul_SkAlphaType;
            }
            // The other non-opaque alpha type should always succeed, but not match.
            test_info(r, codec, info.makeAlphaType(otherAt), SkCodec::kSuccess, NULL);
        }
    }

    // Scanline decoding follows.

    stream.reset(resource(path));
    SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(
            SkScanlineDecoder::NewFromStream(stream.detach()));
    if (supportsScanlineDecoding) {
        bm.eraseColor(SK_ColorYELLOW);
        REPORTER_ASSERT(r, scanlineDecoder);

        REPORTER_ASSERT(r, scanlineDecoder->start(info) == SkCodec::kSuccess);

        for (int y = 0; y < info.height(); y++) {
            result = scanlineDecoder->getScanlines(bm.getAddr(0, y), 1, 0);
            REPORTER_ASSERT(r, result == SkCodec::kSuccess);
        }
        // verify that scanline decoding gives the same result.
        compare_to_good_digest(r, digest, bm);
    } else {
        REPORTER_ASSERT(r, !scanlineDecoder);
    }

    // The rest of this function tests decoding subsets, and will decode an arbitrary number of
    // random subsets.
    // Do not attempt to decode subsets of an image of only once pixel, since there is no
    // meaningful subset.
    if (size.width() * size.height() == 1) {
        return;
    }

    SkRandom rand;
    SkIRect subset;
    SkCodec::Options opts;
    opts.fSubset = &subset;
    for (int i = 0; i < 5; i++) {
        subset = generate_random_subset(&rand, size.width(), size.height());
        SkASSERT(!subset.isEmpty());
        const bool supported = codec->getValidSubset(&subset);
        REPORTER_ASSERT(r, supported == supportsSubsetDecoding);

        SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height());
        SkBitmap bm;
        bm.allocPixels(subsetInfo);
        const SkCodec::Result result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(),
                                                        &opts, NULL, NULL);

        if (supportsSubsetDecoding) {
            REPORTER_ASSERT(r, result == SkCodec::kSuccess);
            // Webp is the only codec that supports subsets, and it will have modified the subset
            // to have even left/top.
            REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
        } else {
            // No subsets will work.
            REPORTER_ASSERT(r, result == SkCodec::kUnimplemented);
        }
    }
}
bool SkBicubicImageFilter::onFilterImage(Proxy* proxy,
                                         const SkBitmap& source,
                                         const SkMatrix& matrix,
                                         SkBitmap* result,
                                         SkIPoint* loc) {
    SkBitmap src = source;
    if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc)) {
        return false;
    }

    if (src.config() != SkBitmap::kARGB_8888_Config) {
        return false;
    }

    SkAutoLockPixels alp(src);
    if (!src.getPixels()) {
        return false;
    }

    SkRect dstRect = SkRect::MakeWH(SkScalarMul(SkIntToScalar(src.width()), fScale.fWidth),
                                    SkScalarMul(SkIntToScalar(src.height()), fScale.fHeight));
    SkIRect dstIRect;
    dstRect.roundOut(&dstIRect);
    if (dstIRect.isEmpty()) {
        return false;
    }
    result->setConfig(src.config(), dstIRect.width(), dstIRect.height());
    result->allocPixels();
    if (!result->getPixels()) {
        return false;
    }

    SkRect srcRect;
    src.getBounds(&srcRect);
    SkMatrix inverse;
    inverse.setRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit);
    inverse.postTranslate(SkFloatToScalar(-0.5f), SkFloatToScalar(-0.5f));

    for (int y = dstIRect.fTop; y < dstIRect.fBottom; ++y) {
        SkPMColor* dptr = result->getAddr32(dstIRect.fLeft, y);
        for (int x = dstIRect.fLeft; x < dstIRect.fRight; ++x) {
            SkPoint srcPt, dstPt = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
            inverse.mapPoints(&srcPt, &dstPt, 1);
            SkScalar fractx = srcPt.fX - SkScalarFloorToScalar(srcPt.fX);
            SkScalar fracty = srcPt.fY - SkScalarFloorToScalar(srcPt.fY);
            int sx = SkScalarFloorToInt(srcPt.fX);
            int sy = SkScalarFloorToInt(srcPt.fY);
            int x0 = SkClampMax(sx - 1, src.width() - 1);
            int x1 = SkClampMax(sx    , src.width() - 1);
            int x2 = SkClampMax(sx + 1, src.width() - 1);
            int x3 = SkClampMax(sx + 2, src.width() - 1);
            int y0 = SkClampMax(sy - 1, src.height() - 1);
            int y1 = SkClampMax(sy    , src.height() - 1);
            int y2 = SkClampMax(sy + 1, src.height() - 1);
            int y3 = SkClampMax(sy + 2, src.height() - 1);
            SkPMColor s00 = *src.getAddr32(x0, y0);
            SkPMColor s10 = *src.getAddr32(x1, y0);
            SkPMColor s20 = *src.getAddr32(x2, y0);
            SkPMColor s30 = *src.getAddr32(x3, y0);
            SkPMColor s0 = cubicBlend(fCoefficients, fractx, s00, s10, s20, s30);
            SkPMColor s01 = *src.getAddr32(x0, y1);
            SkPMColor s11 = *src.getAddr32(x1, y1);
            SkPMColor s21 = *src.getAddr32(x2, y1);
            SkPMColor s31 = *src.getAddr32(x3, y1);
            SkPMColor s1 = cubicBlend(fCoefficients, fractx, s01, s11, s21, s31);
            SkPMColor s02 = *src.getAddr32(x0, y2);
            SkPMColor s12 = *src.getAddr32(x1, y2);
            SkPMColor s22 = *src.getAddr32(x2, y2);
            SkPMColor s32 = *src.getAddr32(x3, y2);
            SkPMColor s2 = cubicBlend(fCoefficients, fractx, s02, s12, s22, s32);
            SkPMColor s03 = *src.getAddr32(x0, y3);
            SkPMColor s13 = *src.getAddr32(x1, y3);
            SkPMColor s23 = *src.getAddr32(x2, y3);
            SkPMColor s33 = *src.getAddr32(x3, y3);
            SkPMColor s3 = cubicBlend(fCoefficients, fractx, s03, s13, s23, s33);
            *dptr++ = cubicBlend(fCoefficients, fracty, s0, s1, s2, s3);
        }
    }
    return true;
}
Example #4
0
sk_sp<SkSpecialImage> SkXfermodeImageFilter::onFilterImage(SkSpecialImage* source,
                                                           const Context& ctx,
                                                           SkIPoint* offset) const {
    SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
    sk_sp<SkSpecialImage> background(this->filterInput(0, source, ctx, &backgroundOffset));

    SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
    sk_sp<SkSpecialImage> foreground(this->filterInput(1, source, ctx, &foregroundOffset));

    SkIRect foregroundBounds = SkIRect::EmptyIRect();
    if (foreground) {
        foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(),
                                             foreground->width(), foreground->height());
    }

    SkIRect srcBounds = SkIRect::EmptyIRect();
    if (background) {
        srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(),
                                       background->width(), background->height());
    }
        
    srcBounds.join(foregroundBounds);
    if (srcBounds.isEmpty()) {
        return nullptr;
    }

    SkIRect bounds;
    if (!this->applyCropRect(ctx, srcBounds, &bounds)) {
        return nullptr;
    }

    offset->fX = bounds.left();
    offset->fY = bounds.top();

#if SK_SUPPORT_GPU
    if (source->isTextureBacked()) {
        return this->filterImageGPU(source,
                                    background, backgroundOffset, 
                                    foreground, foregroundOffset,
                                    bounds);
    }
#endif

    const SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
                                                  kPremul_SkAlphaType);
    sk_sp<SkSpecialSurface> surf(source->makeSurface(info));
    if (!surf) {
        return nullptr;
    }

    SkCanvas* canvas = surf->getCanvas();
    SkASSERT(canvas);

    canvas->clear(0x0); // can't count on background to fully clear the background

    canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));

    SkPaint paint;
    paint.setXfermodeMode(SkXfermode::kSrc_Mode);

    if (background) {
        background->draw(canvas,
                         SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY),
                         &paint);
    }

    paint.setXfermode(fMode);

    if (foreground) {
        foreground->draw(canvas,
                         SkIntToScalar(foregroundOffset.fX), SkIntToScalar(foregroundOffset.fY),
                         &paint);
    }

    canvas->clipRect(SkRect::Make(foregroundBounds), SkRegion::kDifference_Op);
    paint.setColor(SK_ColorTRANSPARENT);
    canvas->drawPaint(paint);

    return surf->makeImageSnapshot();
}
bool SkNinePatchIter::Valid(int width, int height, const SkIRect& center) {
    return !center.isEmpty() && SkIRect::MakeWH(width, height).contains(center);
}
Example #6
0
static void check(skiatest::Reporter* r,
                  const char path[],
                  SkISize size,
                  bool supportsScanlineDecoding,
                  bool supportsSubsetDecoding,
                  bool supports565 = true,
                  bool supportsIncomplete = true) {

    SkAutoTDelete<SkStream> stream(resource(path));
    if (!stream) {
        SkDebugf("Missing resource '%s'\n", path);
        return;
    }

    SkAutoTDelete<SkCodec> codec(nullptr);
    bool isIncomplete = supportsIncomplete;
    if (isIncomplete) {
        size_t size = stream->getLength();
        SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3)));
        codec.reset(SkCodec::NewFromData(data));
    } else {
        codec.reset(SkCodec::NewFromStream(stream.detach()));
    }
    if (!codec) {
        ERRORF(r, "Unable to decode '%s'", path);
        return;
    }

    // Test full image decodes with SkCodec
    SkMD5::Digest codecDigest;
    SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
    SkBitmap bm;
    SkCodec::Result expectedResult = isIncomplete ? SkCodec::kIncompleteInput : SkCodec::kSuccess;
    test_codec(r, codec, bm, info, size, supports565, expectedResult, &codecDigest, nullptr);

    // Scanline decoding follows.
    // Need to call startScanlineDecode() first.
    REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
            == 0);
    REPORTER_ASSERT(r, codec->skipScanlines(1)
            == 0);

    const SkCodec::Result startResult = codec->startScanlineDecode(info);
    if (supportsScanlineDecoding) {
        bm.eraseColor(SK_ColorYELLOW);

        REPORTER_ASSERT(r, startResult == SkCodec::kSuccess);

        for (int y = 0; y < info.height(); y++) {
            const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
            if (!isIncomplete) {
                REPORTER_ASSERT(r, 1 == lines);
            }
        }
        // verify that scanline decoding gives the same result.
        if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) {
            compare_to_good_digest(r, codecDigest, bm);
        }

        // Cannot continue to decode scanlines beyond the end
        REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
                == 0);

        // Interrupting a scanline decode with a full decode starts from
        // scratch
        REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess);
        const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0);
        if (!isIncomplete) {
            REPORTER_ASSERT(r, lines == 1);
        }
        REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes())
                == expectedResult);
        REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
                == 0);
        REPORTER_ASSERT(r, codec->skipScanlines(1)
                == 0);
    } else {
        REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented);
    }

    // The rest of this function tests decoding subsets, and will decode an arbitrary number of
    // random subsets.
    // Do not attempt to decode subsets of an image of only once pixel, since there is no
    // meaningful subset.
    if (size.width() * size.height() == 1) {
        return;
    }

    SkRandom rand;
    SkIRect subset;
    SkCodec::Options opts;
    opts.fSubset = &subset;
    for (int i = 0; i < 5; i++) {
        subset = generate_random_subset(&rand, size.width(), size.height());
        SkASSERT(!subset.isEmpty());
        const bool supported = codec->getValidSubset(&subset);
        REPORTER_ASSERT(r, supported == supportsSubsetDecoding);

        SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height());
        SkBitmap bm;
        bm.allocPixels(subsetInfo);
        const SkCodec::Result result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(),
                                                        &opts, nullptr, nullptr);

        if (supportsSubsetDecoding) {
            REPORTER_ASSERT(r, result == expectedResult);
            // Webp is the only codec that supports subsets, and it will have modified the subset
            // to have even left/top.
            REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
        } else {
            // No subsets will work.
            REPORTER_ASSERT(r, result == SkCodec::kUnimplemented);
        }
    }

    // SkScaledCodec tests
    if ((supportsScanlineDecoding || supportsSubsetDecoding) && supports_scaled_codec(path)) {

        SkAutoTDelete<SkStream> stream(resource(path));
        if (!stream) {
            SkDebugf("Missing resource '%s'\n", path);
            return;
        }

        SkAutoTDelete<SkCodec> codec(nullptr);
        if (isIncomplete) {
            size_t size = stream->getLength();
            SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3)));
            codec.reset(SkScaledCodec::NewFromData(data));
        } else {
            codec.reset(SkScaledCodec::NewFromStream(stream.detach()));
        }
        if (!codec) {
            ERRORF(r, "Unable to decode '%s'", path);
            return;
        }

        SkBitmap bm;
        SkMD5::Digest scaledCodecDigest;
        test_codec(r, codec, bm, info, size, supports565, expectedResult, &scaledCodecDigest,
                &codecDigest);
    }

    // If we've just tested incomplete decodes, let's run the same test again on full decodes.
    if (isIncomplete) {
        check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, supports565, false);
    }
}
Example #7
0
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) {
    GrContext* context = ctxInfo.grContext();
    static const int kW = 10;
    static const int kH = 10;
    static const size_t kRowBytes = sizeof(uint32_t) * kW;

    GrSurfaceDesc baseDesc;
    baseDesc.fConfig = kRGBA_8888_GrPixelConfig;
    baseDesc.fWidth = kW;
    baseDesc.fHeight = kH;

    SkAutoTMalloc<uint32_t> srcPixels(kW * kH);
    for (int i = 0; i < kW * kH; ++i) {
        srcPixels.get()[i] = i;
    }

    SkAutoTMalloc<uint32_t> dstPixels(kW * kH);
    for (int i = 0; i < kW * kH; ++i) {
        dstPixels.get()[i] = ~i;
    }

    static const SkIRect kSrcRects[] {
        { 0,  0, kW  , kH  },
        {-1, -1, kW+1, kH+1},
        { 1,  1, kW-1, kH-1},
        { 5,  5, 6   , 6   },
    };

    static const SkIPoint kDstPoints[] {
        { 0   ,  0   },
        { 1   ,  1   },
        { kW/2,  kH/4},
        { kW-1,  kH-1},
        { kW  ,  kH  },
        { kW+1,  kH+2},
        {-1   , -1   },
    };

    SkAutoTMalloc<uint32_t> read(kW * kH);

    for (auto sOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
        for (auto dOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
            for (auto sFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) {
                for (auto dFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) {
                    for (auto srcRect : kSrcRects) {
                        for (auto dstPoint : kDstPoints) {
                            GrSurfaceDesc srcDesc = baseDesc;
                            srcDesc.fOrigin = sOrigin;
                            srcDesc.fFlags = sFlags;
                            GrSurfaceDesc dstDesc = baseDesc;
                            dstDesc.fOrigin = dOrigin;
                            dstDesc.fFlags = dFlags;

                            SkAutoTUnref<GrTexture> src(
                                context->textureProvider()->createTexture(srcDesc, SkBudgeted::kNo,
                                                                          srcPixels.get(),
                                                                          kRowBytes));
                            SkAutoTUnref<GrTexture> dst(
                                context->textureProvider()->createTexture(dstDesc, SkBudgeted::kNo,
                                                                          dstPixels.get(),
                                                                          kRowBytes));
                            if (!src || !dst) {
                                ERRORF(reporter,
                                       "Could not create surfaces for copy surface test.");
                                continue;
                            }

                            bool result = context->copySurface(dst, src, srcRect, dstPoint);

                            bool expectedResult = true;
                            SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft,
                                                   dstPoint.fY - srcRect.fTop };
                            SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX,
                                                                      dstPoint.fY,
                                                                      srcRect.width(),
                                                                      srcRect.height());

                            SkIRect copiedSrcRect;
                            if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) {
                                expectedResult = false;
                            } else {
                                // If the src rect was clipped, apply same clipping to each side of
                                // copied dst rect.
                                copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft;
                                copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop;
                                copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight;
                                copiedDstRect.fBottom -= copiedSrcRect.fBottom - srcRect.fBottom;
                            }
                            if (copiedDstRect.isEmpty() ||
                                !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) {
                                expectedResult = false;
                            }
                            // To make the copied src rect correct we would apply any dst clipping
                            // back to the src rect, but we don't use it again so don't bother.
                            if (expectedResult != result) {
                                ERRORF(reporter, "Expected return value %d from copySurface, got "
                                       "%d.", expectedResult, result);
                                continue;
                            }

                            if (!expectedResult || !result) {
                                continue;
                            }

                            sk_memset32(read.get(), 0, kW * kH);
                            if (!dst->readPixels(0, 0, kW, kH, baseDesc.fConfig, read.get(),
                                                 kRowBytes)) {
                                ERRORF(reporter, "Error calling readPixels");
                                continue;
                            }

                            bool abort = false;
                            // Validate that pixels inside copiedDstRect received the correct value
                            // from src and that those outside were not modified.
                            for (int y = 0; y < kH && !abort; ++y) {
                                for (int x = 0; x < kW; ++x) {
                                    uint32_t r = read.get()[y * kW + x];
                                    if (copiedDstRect.contains(x, y)) {
                                        int sx = x - dstOffset.fX;
                                        int sy = y - dstOffset.fY;
                                        uint32_t s = srcPixels.get()[sy * kW + sx];
                                        if (s != r) {
                                            ERRORF(reporter, "Expected dst %d,%d to contain "
                                                   "0x%08x copied from src location %d,%d. Got "
                                                   "0x%08x", x, y, s, sx, sy, r);
                                            abort = true;
                                            break;
                                        }
                                    } else {
                                        uint32_t d = dstPixels.get()[y * kW + x];
                                        if (d != r) {
                                            ERRORF(reporter, "Expected dst %d,%d to be unmodified ("
                                                   "0x%08x). Got 0x%08x", x, y, d, r);
                                            abort = true;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
Example #8
0
static void convolve_gaussian(GrDrawContext* drawContext,
                              const GrClip& clip,
                              const SkIRect& srcRect,
                              GrTexture* texture,
                              Gr1DKernelEffect::Direction direction,
                              int radius,
                              float sigma,
                              const SkIRect* srcBounds,
                              const SkIPoint& srcOffset) {
    float bounds[2] = { 0.0f, 1.0f };
    SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height());
    if (!srcBounds) {
        convolve_gaussian_1d(drawContext, clip, dstRect, srcOffset, texture,
                             direction, radius, sigma, false, bounds);
        return;
    }
    SkIRect midRect = *srcBounds, leftRect, rightRect;
    midRect.offset(srcOffset);
    SkIRect topRect, bottomRect;
    if (direction == Gr1DKernelEffect::kX_Direction) {
        bounds[0] = SkIntToFloat(srcBounds->left()) / texture->width();
        bounds[1] = SkIntToFloat(srcBounds->right()) / texture->width();
        topRect = SkIRect::MakeLTRB(0, 0, dstRect.right(), midRect.top());
        bottomRect = SkIRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dstRect.bottom());
        midRect.inset(radius, 0);
        leftRect = SkIRect::MakeLTRB(0, midRect.top(), midRect.left(), midRect.bottom());
        rightRect =
            SkIRect::MakeLTRB(midRect.right(), midRect.top(), dstRect.width(), midRect.bottom());
        dstRect.fTop = midRect.top();
        dstRect.fBottom = midRect.bottom();
    } else {
        bounds[0] = SkIntToFloat(srcBounds->top()) / texture->height();
        bounds[1] = SkIntToFloat(srcBounds->bottom()) / texture->height();
        topRect = SkIRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom());
        bottomRect = SkIRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstRect.bottom());
        midRect.inset(0, radius);
        leftRect = SkIRect::MakeLTRB(midRect.left(), 0, midRect.right(), midRect.top());
        rightRect =
            SkIRect::MakeLTRB(midRect.left(), midRect.bottom(), midRect.right(), dstRect.height());
        dstRect.fLeft = midRect.left();
        dstRect.fRight = midRect.right();
    }
    if (!topRect.isEmpty()) {
        drawContext->clear(&topRect, 0, false);
    }

    if (!bottomRect.isEmpty()) {
        drawContext->clear(&bottomRect, 0, false);
    }
    if (midRect.isEmpty()) {
        // Blur radius covers srcBounds; use bounds over entire draw
        convolve_gaussian_1d(drawContext, clip, dstRect, srcOffset, texture,
                             direction, radius, sigma, true, bounds);
    } else {
        // Draw right and left margins with bounds; middle without.
        convolve_gaussian_1d(drawContext, clip, leftRect, srcOffset, texture,
                             direction, radius, sigma, true, bounds);
        convolve_gaussian_1d(drawContext, clip, rightRect, srcOffset, texture,
                             direction, radius, sigma, true, bounds);
        convolve_gaussian_1d(drawContext, clip, midRect, srcOffset, texture,
                             direction, radius, sigma, false, bounds);
    }
}
Example #9
0
static bool clip_bounds_quick_reject(const SkIRect& clipBounds, const SkIRect& rect) {
    return clipBounds.isEmpty() || rect.isEmpty() || !SkIRect::Intersects(clipBounds, rect);
}
Example #10
0
void Image::paintSkBitmap(GraphicsContext* context, const NativeImageSkia& bitmap, const SkRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
{
    TRACE_EVENT0("skia", "paintSkBitmap");
    SkPaint paint;
    paint.setXfermodeMode(compOp);
    paint.setAlpha(context->getNormalizedAlpha());
    paint.setLooper(context->drawLooper());
    // only antialias if we're rotated or skewed
    paint.setAntiAlias(hasNon90rotation(context));

    ResamplingMode resampling;
    if (context->isAccelerated())
        resampling = RESAMPLE_LINEAR;
    else if (context->printing())
        resampling = RESAMPLE_NONE;
    else {
        // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale).
        SkRect destRectTarget = destRect;
        if (!(context->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
            context->getTotalMatrix().mapRect(&destRectTarget, destRect);

        resampling = computeResamplingMode(context->getTotalMatrix(), bitmap,
            SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
            SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()));
    }

    if (resampling == RESAMPLE_NONE) {
        // FIXME: This is to not break tests (it results in the filter bitmap flag
        // being set to true). We need to decide if we respect RESAMPLE_NONE
        // being returned from computeResamplingMode.
        resampling = RESAMPLE_LINEAR;
    }
    resampling = limitResamplingMode(context, resampling);
    paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
    if (resampling == RESAMPLE_AWESOME)
        drawResampledBitmap(context, paint, bitmap, srcRect, destRect);
    else {
        // No resampling necessary, we can just draw the bitmap. We want to
        // filter it if we decided to do linear interpolation above, or if there
        // is something interesting going on with the matrix (like a rotation).
        // Note: for serialization, we will want to subset the bitmap first so
        // we don't send extra pixels.
        SkIRect enclosingSrcRect;
        SkRect enclosingDestRect;
        SkISize bitmapSize = SkISize::Make(bitmap.bitmap().width(), bitmap.bitmap().height());
        bool needsClipping = computeBitmapDrawRects(bitmapSize, srcRect, destRect, &enclosingSrcRect, &enclosingDestRect);

        if (enclosingSrcRect.isEmpty() || enclosingDestRect.isEmpty())
            return;

        // If destination is enlarged because source rectangle didn't align to
        // integer boundaries then we draw a slightly larger rectangle and clip
        // to the original destination rectangle.
        // See http://crbug.com/145540.
        if (needsClipping) {
            context->save();
            context->clipRect(destRect);
        }

        context->drawBitmapRect(bitmap.bitmap(), &enclosingSrcRect, enclosingDestRect, &paint);

        if (needsClipping)
            context->restore();
    }
    context->didDrawRect(destRect, paint, &bitmap.bitmap());
}
sk_sp<SkSpecialImage> SkMergeImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx,
                                                        SkIPoint* offset) const {
    int inputCount = this->countInputs();
    if (inputCount < 1) {
        return nullptr;
    }

    SkIRect bounds;
    bounds.setEmpty();

    SkAutoTDeleteArray<sk_sp<SkSpecialImage>> inputs(new sk_sp<SkSpecialImage>[inputCount]);
    SkAutoTDeleteArray<SkIPoint> offsets(new SkIPoint[inputCount]);

    // Filter all of the inputs.
    for (int i = 0; i < inputCount; ++i) {
        offsets[i].setZero();
        inputs[i] = this->filterInput(i, source, ctx, &offsets[i]);
        if (!inputs[i]) {
            continue;
        }
        const SkIRect inputBounds = SkIRect::MakeXYWH(offsets[i].fX, offsets[i].fY,
                                                      inputs[i]->width(), inputs[i]->height());
        bounds.join(inputBounds);
    }
    if (bounds.isEmpty()) {
        return nullptr;
    }

    // Apply the crop rect to the union of the inputs' bounds.
    // Note that the crop rect can only reduce the bounds, since this
    // filter does not affect transparent black.
    bool embiggen = false;
    this->getCropRect().applyTo(bounds, ctx.ctm(), embiggen, &bounds);
    if (!bounds.intersect(ctx.clipBounds())) {
        return nullptr;
    }

    const int x0 = bounds.left();
    const int y0 = bounds.top();

    sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size()));
    if (!surf) {
        return nullptr;
    }

    SkCanvas* canvas = surf->getCanvas();
    SkASSERT(canvas);

    canvas->clear(0x0);

    // Composite all of the filter inputs.
    for (int i = 0; i < inputCount; ++i) {
        if (!inputs[i]) {
            continue;
        }

        SkPaint paint;
        if (fModes) {
            paint.setBlendMode((SkBlendMode)fModes[i]);
        }

        inputs[i]->draw(canvas,
                        SkIntToScalar(offsets[i].x() - x0), SkIntToScalar(offsets[i].y() - y0),
                        &paint);
    }

    offset->fX = bounds.left();
    offset->fY = bounds.top();
    return surf->makeImageSnapshot();
}
Example #12
0
static void testTightBoundsQuads(PathOpsThreadState* data) {
    SkRandom ran;
    const int bitWidth = 32;
    const int bitHeight = 32;
    const float pathMin = 1;
    const float pathMax = (float) (bitHeight - 2);
    SkBitmap& bits = *data->fBitmap;
    if (bits.width() == 0) {
        bits.allocN32Pixels(bitWidth, bitHeight);
    }
    SkCanvas canvas(bits);
    SkPaint paint;
    for (int index = 0; index < 100; ++index) {
        SkPath path;
        int contourCount = ran.nextRangeU(1, 10);
        for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
            int lineCount = ran.nextRangeU(1, 10);
            path.moveTo(ran.nextRangeF(1, pathMax), ran.nextRangeF(pathMin, pathMax));
            for (int lIndex = 0; lIndex < lineCount; ++lIndex) {
                if (ran.nextBool()) {
                    path.lineTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax));
                } else {
                    path.quadTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax),
                            ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax));
                }
            }
            if (ran.nextBool()) {
                path.close();
            }
        }
        SkRect classicBounds = path.getBounds();
        SkRect tightBounds;
        REPORTER_ASSERT(data->fReporter, TightBounds(path, &tightBounds));
        REPORTER_ASSERT(data->fReporter, classicBounds.contains(tightBounds));
        canvas.drawColor(SK_ColorWHITE);
        canvas.drawPath(path, paint);
        SkIRect bitsWritten = {31, 31, 0, 0};
        for (int y = 0; y < bitHeight; ++y) {
            uint32_t* addr1 = data->fBitmap->getAddr32(0, y);
            bool lineWritten = false;
            for (int x = 0; x < bitWidth; ++x) {
                if (addr1[x] == (uint32_t) -1) {
                    continue;
                }
                lineWritten = true;
                bitsWritten.fLeft = SkTMin(bitsWritten.fLeft, x);
                bitsWritten.fRight = SkTMax(bitsWritten.fRight, x);
            }
            if (!lineWritten) {
                continue;
            }
            bitsWritten.fTop = SkTMin(bitsWritten.fTop, y);
            bitsWritten.fBottom = SkTMax(bitsWritten.fBottom, y);
        }
        if (!bitsWritten.isEmpty()) {
            SkIRect tightOut;
            tightBounds.roundOut(&tightOut);
            REPORTER_ASSERT(data->fReporter, tightOut.contains(bitsWritten));
        }
    }
}
void GLExtras::drawRegion(const SkRegion& region, bool fill,
                          bool drawBorder, bool useDark)
{
    if (region.isEmpty())
        return;
    if (fill) {
        SkRegion::Iterator rgnIter(region);
        while (!rgnIter.done()) {
            const SkIRect& ir = rgnIter.rect();
            SkRect r;
            r.set(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom);
            if (useDark)
                drawRing(r, COLOR_HOLO_DARK);
            else
                drawRing(r, COLOR_HOLO_LIGHT);
            rgnIter.next();
        }
    }
    if (fill && !drawBorder)
        return;
    SkPath path;
    if (!region.getBoundaryPath(&path))
        return;
    SkPath::Iter iter(path, true);
    SkPath::Verb verb;
    SkPoint pts[4];
    SkRegion clip;
    SkIRect startRect;
    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
        if (verb == SkPath::kLine_Verb) {
            SkRect r;
            r.set(pts, 2);
            SkIRect line;
            int borderWidth = RING_BORDER_WIDTH;
            if (!fill)
                borderWidth *= 2;
            line.fLeft = r.fLeft - borderWidth;
            line.fRight = r.fRight + borderWidth;
            line.fTop = r.fTop - borderWidth;
            line.fBottom = r.fBottom + borderWidth;
            if (clip.intersects(line)) {
                clip.op(line, SkRegion::kReverseDifference_Op);
                if (clip.isEmpty())
                    continue; // Nothing to draw, continue
                line = clip.getBounds();
                if (SkIRect::Intersects(startRect, line)) {
                    clip.op(startRect, SkRegion::kDifference_Op);
                    if (clip.isEmpty())
                        continue; // Nothing to draw, continue
                    line = clip.getBounds();
                }
            } else {
                clip.setRect(line);
            }
            r.set(line.fLeft, line.fTop, line.fRight, line.fBottom);
            if (useDark)
                drawRing(r, COLOR_HOLO_DARK);
            else
                drawRing(r, COLOR_HOLO_LIGHT);
            if (startRect.isEmpty()) {
                startRect.set(line.fLeft, line.fTop, line.fRight, line.fBottom);
            }
        }
        if (verb == SkPath::kMove_Verb) {
            startRect.setEmpty();
        }
    }
}