Beispiel #1
0
static SkBitmap make_bmp(int w, int h) {
    SkBitmap bmp;
    bmp.allocN32Pixels(w, h, true);

    SkCanvas canvas(bmp);
    SkScalar wScalar = SkIntToScalar(w);
    SkScalar hScalar = SkIntToScalar(h);

    SkPoint     pt = { wScalar / 2, hScalar / 2 };

    SkScalar    radius = 3 * SkMaxScalar(wScalar, hScalar);

    SkColor     colors[] = { SK_ColorDKGRAY, 0xFF222255,
                             0xFF331133, 0xFF884422,
                             0xFF000022, SK_ColorWHITE,
                             0xFFAABBCC};

    SkScalar    pos[] = {0,
                         SK_Scalar1 / 6,
                         2 * SK_Scalar1 / 6,
                         3 * SK_Scalar1 / 6,
                         4 * SK_Scalar1 / 6,
                         5 * SK_Scalar1 / 6,
                         SK_Scalar1};

    SkPaint paint;
    SkRect rect = SkRect::MakeWH(wScalar, hScalar);
    SkMatrix mat = SkMatrix::I();
    for (int i = 0; i < 4; ++i) {
        paint.setShader(SkGradientShader::CreateRadial(
                        pt, radius,
                        colors, pos,
                        SK_ARRAY_COUNT(colors),
                        SkShader::kRepeat_TileMode,
                        0, &mat))->unref();
        canvas.drawRect(rect, paint);
        rect.inset(wScalar / 8, hScalar / 8);
        mat.preTranslate(6 * wScalar, 6 * hScalar);
        mat.postScale(SK_Scalar1 / 3, SK_Scalar1 / 3);
    }

    paint.setAntiAlias(true);
    sk_tool_utils::set_portable_typeface(&paint);
    paint.setTextSize(wScalar / 2.2f);
    paint.setShader(0);
    paint.setColor(SK_ColorLTGRAY);
    static const char kTxt[] = "Skia";
    SkPoint texPos = { wScalar / 17, hScalar / 2 + paint.getTextSize() / 2.5f };
    canvas.drawText(kTxt, SK_ARRAY_COUNT(kTxt)-1, texPos.fX, texPos.fY, paint);
    paint.setColor(SK_ColorBLACK);
    paint.setStyle(SkPaint::kStroke_Style);
    paint.setStrokeWidth(SK_Scalar1);
    canvas.drawText(kTxt, SK_ARRAY_COUNT(kTxt)-1, texPos.fX, texPos.fY, paint);
    return bmp;
}
Beispiel #2
0
// Creates a bitmap and a matching image.
static sk_sp<SkImage> makebm(SkCanvas* origCanvas, SkBitmap* resultBM, int w, int h) {
    SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);

    auto surface(sk_tool_utils::makeSurface(origCanvas, info));
    SkCanvas* canvas = surface->getCanvas();

    canvas->clear(SK_ColorTRANSPARENT);

    SkScalar wScalar = SkIntToScalar(w);
    SkScalar hScalar = SkIntToScalar(h);

    SkPoint     pt = { wScalar / 2, hScalar / 2 };

    SkScalar    radius = 4 * SkMaxScalar(wScalar, hScalar);

    SkColor     colors[] = { SK_ColorRED, SK_ColorYELLOW,
                             SK_ColorGREEN, SK_ColorMAGENTA,
                             SK_ColorBLUE, SK_ColorCYAN,
                             SK_ColorRED};

    SkScalar    pos[] = {0,
                         SK_Scalar1 / 6,
                         2 * SK_Scalar1 / 6,
                         3 * SK_Scalar1 / 6,
                         4 * SK_Scalar1 / 6,
                         5 * SK_Scalar1 / 6,
                         SK_Scalar1};

    SkPaint     paint;
    SkRect rect = SkRect::MakeWH(wScalar, hScalar);
    SkMatrix mat = SkMatrix::I();
    for (int i = 0; i < 4; ++i) {
        paint.setShader(SkGradientShader::MakeRadial(
                        pt, radius,
                        colors, pos,
                        SK_ARRAY_COUNT(colors),
                        SkShader::kRepeat_TileMode,
                        0, &mat));
        canvas->drawRect(rect, paint);
        rect.inset(wScalar / 8, hScalar / 8);
        mat.postScale(SK_Scalar1 / 4, SK_Scalar1 / 4);
    }

    auto image = surface->makeImageSnapshot();

    SkBitmap tempBM;

    image->asLegacyBitmap(&tempBM);

    // Let backends know we won't change this, so they don't have to deep copy it defensively.
    tempBM.setImmutable();
    *resultBM = tempBM;

    return image;
}
Beispiel #3
0
static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
    SkVector    vec = pts[1] - pts[0];
    SkScalar    mag = vec.length();
    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;

    vec.scale(inv);
    SkMatrix matrix;
    matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
    matrix.postTranslate(-pts[0].fX, -pts[0].fY);
    matrix.postScale(inv, inv);
    return matrix;
}
Beispiel #4
0
SkGLDevice::TexCache* SkGLDevice::setupGLPaintShader(const SkPaint& paint) {
    SkGL::SetPaint(paint);
    
    SkShader* shader = paint.getShader();
    if (NULL == shader) {
        return NULL;
    }
    
    if (!shader->setContext(this->accessBitmap(false), paint, this->matrix())) {
        return NULL;
    }
    
    SkBitmap bitmap;
    SkMatrix matrix;
    SkShader::TileMode tileModes[2];
    if (!shader->asABitmap(&bitmap, &matrix, tileModes)) {
        return NULL;
    }
    
    bitmap.lockPixels();
    if (!bitmap.readyToDraw()) {
        return NULL;
    }
    
    // see if we've already cached the bitmap from the shader
    SkPoint max;
    GLuint name;
    TexCache* cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
    // the lock has already called glBindTexture for us
    SkGL::SetTexParams(paint.isFilterBitmap(), tileModes[0], tileModes[1]);
    
    // since our texture coords will be in local space, we wack the texture
    // matrix to map them back into 0...1 before we load it
    SkMatrix localM;
    if (shader->getLocalMatrix(&localM)) {
        SkMatrix inverse;
        if (localM.invert(&inverse)) {
            matrix.preConcat(inverse);
        }
    }
    
    matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
    glMatrixMode(GL_TEXTURE);
    SkGL::LoadMatrix(matrix);
    glMatrixMode(GL_MODELVIEW);
    
    // since we're going to use a shader/texture, we don't want the color,
    // just its alpha
    SkGL::SetAlpha(paint.getAlpha());
    // report that we have setup the texture
    return cache;
}
Beispiel #5
0
std::unique_ptr<GrFragmentProcessor> GrTextureAdjuster::createFragmentProcessor(
        const SkMatrix& origTextureMatrix,
        const SkRect& constraintRect,
        FilterConstraint filterConstraint,
        bool coordsLimitedToConstraintRect,
        const GrSamplerState::Filter* filterOrNullForBicubic,
        SkColorSpace* dstColorSpace) {
    SkMatrix textureMatrix = origTextureMatrix;

    SkRect domain;
    GrSamplerState samplerState;
    if (filterOrNullForBicubic) {
        samplerState.setFilterMode(*filterOrNullForBicubic);
    }
    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
    sk_sp<GrTextureProxy> proxy(
            this->refTextureProxySafeForParams(samplerState, scaleAdjust));
    if (!proxy) {
        return nullptr;
    }
    // If we made a copy then we only copied the contentArea, in which case the new texture is all
    // content.
    if (proxy.get() != this->originalProxy()) {
        textureMatrix.postScale(scaleAdjust[0], scaleAdjust[1]);
    }

    DomainMode domainMode =
        DetermineDomainMode(constraintRect, filterConstraint, coordsLimitedToConstraintRect,
                            proxy.get(), filterOrNullForBicubic, &domain);
    if (kTightCopy_DomainMode == domainMode) {
        // TODO: Copy the texture and adjust the texture matrix (both parts need to consider
        // non-int constraint rect)
        // For now: treat as bilerp and ignore what goes on above level 0.

        // We only expect MIP maps to require a tight copy.
        SkASSERT(filterOrNullForBicubic &&
                 GrSamplerState::Filter::kMipMap == *filterOrNullForBicubic);
        static const GrSamplerState::Filter kBilerp = GrSamplerState::Filter::kBilerp;
        domainMode =
            DetermineDomainMode(constraintRect, filterConstraint, coordsLimitedToConstraintRect,
                                proxy.get(), &kBilerp, &domain);
        SkASSERT(kTightCopy_DomainMode != domainMode);
    }
    SkASSERT(kNoDomain_DomainMode == domainMode ||
             (domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom));
    GrPixelConfig config = proxy->config();
    auto fp = CreateFragmentProcessorForDomainAndFilter(std::move(proxy), textureMatrix,
                                                        domainMode, domain, filterOrNullForBicubic);
    return GrColorSpaceXformEffect::Make(std::move(fp), fColorSpace, config, dstColorSpace);
}
Beispiel #6
0
std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
        const GrFPArgs& args) const {
    const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix);
    SkMatrix lmInverse;
    if (!lm->invert(&lmInverse)) {
        return nullptr;
    }

    GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX),
                                            tile_mode_to_wrap_mode(fTileModeY)};

    // Must set wrap and filter on the sampler before requesting a texture. In two places below
    // we check the matrix scale factors to determine how to interpret the filter quality setting.
    // This completely ignores the complexity of the drawVertices case where explicit local coords
    // are provided by the caller.
    bool doBicubic;
    GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
            args.fFilterQuality, *args.fViewMatrix, *lm,
            args.fContext->contextPriv().sharpenMipmappedTextures(), &doBicubic);
    GrSamplerState samplerState(wrapModes, textureFilterMode);
    sk_sp<SkColorSpace> texColorSpace;
    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
    sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(
            args.fContext, samplerState, args.fDstColorSpaceInfo->colorSpace(), &texColorSpace,
            scaleAdjust));
    if (!proxy) {
        return nullptr;
    }

    GrPixelConfig config = proxy->config();
    bool isAlphaOnly = GrPixelConfigIsAlphaOnly(config);

    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);

    std::unique_ptr<GrFragmentProcessor> inner;
    if (doBicubic) {
        inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes);
    } else {
        inner = GrSimpleTextureEffect::Make(std::move(proxy), lmInverse, samplerState);
    }
    inner = GrColorSpaceXformEffect::Make(std::move(inner), texColorSpace.get(), config,
                                          args.fDstColorSpaceInfo->colorSpace());
    if (isAlphaOnly) {
        return inner;
    }
    return GrFragmentProcessor::MulChildByInputAlpha(std::move(inner));
}
static SkImage* makebm(SkBitmap* bm, int w, int h) {
    bm->allocN32Pixels(w, h);
    bm->eraseColor(SK_ColorTRANSPARENT);

    SkCanvas    canvas(*bm);

    SkScalar wScalar = SkIntToScalar(w);
    SkScalar hScalar = SkIntToScalar(h);

    SkPoint     pt = { wScalar / 2, hScalar / 2 };

    SkScalar    radius = 4 * SkMaxScalar(wScalar, hScalar);

    SkColor     colors[] = { SK_ColorRED, SK_ColorYELLOW,
                             SK_ColorGREEN, SK_ColorMAGENTA,
                             SK_ColorBLUE, SK_ColorCYAN,
                             SK_ColorRED};

    SkScalar    pos[] = {0,
                         SK_Scalar1 / 6,
                         2 * SK_Scalar1 / 6,
                         3 * SK_Scalar1 / 6,
                         4 * SK_Scalar1 / 6,
                         5 * SK_Scalar1 / 6,
                         SK_Scalar1};

    SkPaint     paint;
    SkRect rect = SkRect::MakeWH(wScalar, hScalar);
    SkMatrix mat = SkMatrix::I();
    for (int i = 0; i < 4; ++i) {
        paint.setShader(SkGradientShader::CreateRadial(
                        pt, radius,
                        colors, pos,
                        SK_ARRAY_COUNT(colors),
                        SkShader::kRepeat_TileMode,
                        0, &mat))->unref();
        canvas.drawRect(rect, paint);
        rect.inset(wScalar / 8, hScalar / 8);
        mat.postScale(SK_Scalar1 / 4, SK_Scalar1 / 4);
    }
    // Let backends know we won't change this, so they don't have to deep copy it defensively.
    bm->setImmutable();

    return image_from_bitmap(*bm);
}
static SkImage* makebm(SkCanvas* caller, int w, int h) {
    SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
    SkAutoTUnref<SkSurface> surface(caller->newSurface(info));
    if (nullptr == surface) {
        surface.reset(SkSurface::NewRaster(info));
    }
    SkCanvas* canvas = surface->getCanvas();

    const SkScalar wScalar = SkIntToScalar(w);
    const SkScalar hScalar = SkIntToScalar(h);

    const SkPoint     pt = { wScalar / 2, hScalar / 2 };

    const SkScalar    radius = 4 * SkMaxScalar(wScalar, hScalar);

    static const SkColor     colors[] = { SK_ColorRED, SK_ColorYELLOW,
                                          SK_ColorGREEN, SK_ColorMAGENTA,
                                          SK_ColorBLUE, SK_ColorCYAN,
                                          SK_ColorRED};

    static const SkScalar    pos[] = {0,
                                      SK_Scalar1 / 6,
                                      2 * SK_Scalar1 / 6,
                                      3 * SK_Scalar1 / 6,
                                      4 * SK_Scalar1 / 6,
                                      5 * SK_Scalar1 / 6,
                                      SK_Scalar1};

    SkASSERT(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos));
    SkPaint     paint;
    SkRect rect = SkRect::MakeWH(wScalar, hScalar);
    SkMatrix mat = SkMatrix::I();
    for (int i = 0; i < 4; ++i) {
        paint.setShader(SkGradientShader::CreateRadial(
                        pt, radius,
                        colors, pos,
                        SK_ARRAY_COUNT(colors),
                        SkShader::kRepeat_TileMode,
                        0, &mat))->unref();
        canvas->drawRect(rect, paint);
        rect.inset(wScalar / 8, hScalar / 8);
        mat.postScale(SK_Scalar1 / 4, SK_Scalar1 / 4);
    }
    return surface->newImageSnapshot();
}
Beispiel #9
0
TwoPointConicalEffect::Data::Data(const SkTwoPointConicalGradient& shader, SkMatrix& matrix) {
    fType = shader.getType();
    if (fType == Type::kRadial) {
        // Map center to (0, 0)
        matrix.postTranslate(-shader.getStartCenter().fX, -shader.getStartCenter().fY);

        // scale |fDiffRadius| to 1
        SkScalar dr = shader.getDiffRadius();
        matrix.postScale(1 / dr, 1 / dr);
        fRadius0 = shader.getStartRadius() / dr;
        fDiffRadius = dr < 0 ? -1 : 1;
    } else if (fType == Type::kStrip) {
        fRadius0 = shader.getStartRadius() / shader.getCenterX1();
        fDiffRadius = 0;
        matrix.postConcat(shader.getGradientMatrix());
    } else if (fType == Type::kFocal) {
        fFocalData = shader.getFocalData();
        matrix.postConcat(shader.getGradientMatrix());
    }
}
Beispiel #10
0
SkMatrix Viewer::computeMatrix() {
    SkMatrix m;
    m.reset();

    if (fZoomLevel) {
        SkPoint center;
        //m = this->getLocalMatrix();//.invert(&m);
        m.mapXY(fZoomCenterX, fZoomCenterY, &center);
        SkScalar cx = center.fX;
        SkScalar cy = center.fY;

        m.setTranslate(-cx, -cy);
        m.postScale(fZoomScale, fZoomScale);
        m.postTranslate(cx, cy);
    }

    m.preConcat(fGesture.localM());
    m.preConcat(fGesture.globalM());

    return m;
}
Beispiel #11
0
    void onOnceBeforeDraw() override {
        SkPictureRecorder recorder;
        SkCanvas* pictureCanvas = recorder.beginRecording(kPictureSize, kPictureSize);
        draw_scene(pictureCanvas, kPictureSize);
        SkAutoTUnref<SkPicture> picture(recorder.endRecording());

        SkPoint offset = SkPoint::Make(100, 100);
        pictureCanvas = recorder.beginRecording(SkRect::MakeXYWH(offset.x(), offset.y(),
                                                                 kPictureSize, kPictureSize));
        pictureCanvas->translate(offset.x(), offset.y());
        draw_scene(pictureCanvas, kPictureSize);
        SkAutoTUnref<SkPicture> offsetPicture(recorder.endRecording());

        for (unsigned i = 0; i < SK_ARRAY_COUNT(tiles); ++i) {
            SkRect tile = SkRect::MakeXYWH(tiles[i].x * kPictureSize,
                                           tiles[i].y * kPictureSize,
                                           tiles[i].w * kPictureSize,
                                           tiles[i].h * kPictureSize);
            SkMatrix localMatrix;
            localMatrix.setTranslate(tiles[i].offsetX * kPictureSize,
                                     tiles[i].offsetY * kPictureSize);
            localMatrix.postScale(kFillSize / (2 * kPictureSize),
                                  kFillSize / (2 * kPictureSize));

            SkPicture* picturePtr = picture.get();
            SkRect* tilePtr = &tile;

            if (tile == SkRect::MakeWH(kPictureSize, kPictureSize)) {
                // When the tile == picture bounds, exercise the picture + offset path.
                picturePtr = offsetPicture.get();
                tilePtr = NULL;
            }

            fShaders[i].reset(SkShader::CreatePictureShader(picturePtr,
                                                            SkShader::kRepeat_TileMode,
                                                            SkShader::kRepeat_TileMode,
                                                            &localMatrix,
                                                            tilePtr));
        }
    }
    void onDraw(SkCanvas* canvas) override {
        SkIRect rects[2];
        rects[0] = SkIRect::MakeXYWH(0, 150, WIDTH, HEIGHT - 300);
        rects[1] = SkIRect::MakeXYWH(150, 0, WIDTH - 300, HEIGHT);
        SkRegion region;
        region.setRects(rects, 2);

        SkMatrix matrix;
        matrix.reset();
        matrix.setTranslate(WIDTH * .1f, HEIGHT * .1f);
        matrix.postScale(.8f, .8f);

        canvas->concat(matrix);

        SkPaint paint;
        paint.setImageFilter(
            SkAlphaThresholdFilter::Create(region, 0.2f, 0.7f))->unref();
        canvas->saveLayer(nullptr, &paint);
        paint.setAntiAlias(true);

        SkPaint rect_paint;
        rect_paint.setColor(0xFF0000FF);
        canvas->drawRect(SkRect::MakeXYWH(0, 0, WIDTH / 2, HEIGHT / 2),
                         rect_paint);
        rect_paint.setColor(0xBFFF0000);
        canvas->drawRect(SkRect::MakeXYWH(WIDTH / 2, 0, WIDTH / 2, HEIGHT / 2),
                         rect_paint);
        rect_paint.setColor(0x3F00FF00);
        canvas->drawRect(SkRect::MakeXYWH(0, HEIGHT / 2, WIDTH / 2, HEIGHT / 2),
                         rect_paint);
        rect_paint.setColor(0x00000000);
        canvas->drawRect(SkRect::MakeXYWH(WIDTH / 2, HEIGHT / 2, WIDTH / 2, HEIGHT / 2),
                         rect_paint);

        canvas->restore();
    }
static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
    SkMatrix mat;

    // identity
    mat.setIdentity();
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // translation only
    mat.reset();
    mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with same size
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with one negative
    mat.reset();
    mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with different size
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // scale with same size at a pivot point
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
                 SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with different size at a pivot point
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
                 SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with same size
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with different size
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with same size at a pivot point
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
                SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with different size at a pivot point
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
                SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // perspective x
    mat.reset();
    mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // perspective y
    mat.reset();
    mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

#ifdef SK_SCALAR_IS_FLOAT
    /* We bypass the following tests for SK_SCALAR_IS_FIXED build.
     * The long discussion can be found in this issue:
     *     http://codereview.appspot.com/5999050/
     * In short, we haven't found a perfect way to fix the precision
     * issue, i.e. the way we use tolerance in isSimilarityTransformation
     * is incorrect. The situation becomes worse in fixed build, so
     * we disabled rotation related tests for fixed build.
     */

    // rotate
    for (int angle = 0; angle < 360; ++angle) {
        mat.reset();
        mat.setRotate(SkIntToScalar(angle));
        REPORTER_ASSERT(reporter, mat.isSimilarity());
    }

    // see if there are any accumulated precision issues
    mat.reset();
    for (int i = 1; i < 360; i++) {
        mat.postRotate(SkIntToScalar(1));
    }
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // rotate + translate
    mat.reset();
    mat.setRotate(SkIntToScalar(30));
    mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // rotate + uniform scale
    mat.reset();
    mat.setRotate(SkIntToScalar(30));
    mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // rotate + non-uniform scale
    mat.reset();
    mat.setRotate(SkIntToScalar(30));
    mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());
#endif

    // all zero
    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // all zero except perspective
    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // scales zero, only skews
    mat.setAll(0, SK_Scalar1, 0,
               SK_Scalar1, 0, 0,
               0, 0, SkMatrix::I()[8]);
    REPORTER_ASSERT(reporter, mat.isSimilarity());
}
void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
                                        const GrClip& clip,
                                        const GrPaint& paint,
                                        const SkPaint& skPaint,
                                        size_t textByteLength,
                                        RenderMode renderMode,
                                        const SkMatrix& viewMatrix,
                                        const SkIRect& regionClipBounds) {
    GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds);

    fContextInitialMatrix = viewMatrix;
    fViewMatrix = viewMatrix;
    fLocalMatrix = SkMatrix::I();

    const bool otherBackendsWillDrawAsPaths =
        SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);

    fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
                              kMaxAccuracy_RenderMode == renderMode &&
                              SkToBool(fContextInitialMatrix.getType() &
                                       (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));

    if (fUsingDeviceSpaceGlyphs) {
        // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
        SkASSERT(!fContextInitialMatrix.hasPerspective());

        // The whole shape (including stroke) will be baked into the glyph outlines. Make
        // NVPR just fill the baked shapes.
        fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle);

        fTextRatio = fTextInverseRatio = 1.0f;

        // Glyphs loaded by GPU path rendering have an inverted y-direction.
        SkMatrix m;
        m.setScale(1, -1);
        fViewMatrix = m;

        // Post-flip the initial matrix so we're left with just the flip after
        // the paint preConcats the inverse.
        m = fContextInitialMatrix;
        m.postScale(1, -1);
        if (!m.invert(&fLocalMatrix)) {
            SkDebugf("Not invertible!\n");
            return;
        }

        fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix,
                                           true /*ignoreGamma*/);
        fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
                                &fGlyphCache->getDescriptor(), fStroke);
    } else {
        // Don't bake strokes into the glyph outlines. We will stroke the glyphs
        // using the GPU instead. This is the fast path.
        fStroke = GrStrokeInfo(fSkPaint);
        fSkPaint.setStyle(SkPaint::kFill_Style);

        if (fStroke.isHairlineStyle()) {
            // Approximate hairline stroke.
            SkScalar strokeWidth = SK_Scalar1 /
                (SkVector::Make(fContextInitialMatrix.getScaleX(),
                                fContextInitialMatrix.getSkewY()).length());
            fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);

        } else if (fSkPaint.isFakeBoldText() &&
#ifdef SK_USE_FREETYPE_EMBOLDEN
                   kMaxPerformance_RenderMode == renderMode &&
#endif
                   SkStrokeRec::kStroke_Style != fStroke.getStyle()) {

            // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
            SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
                                                        kStdFakeBoldInterpKeys,
                                                        kStdFakeBoldInterpValues,
                                                        kStdFakeBoldInterpLength);
            SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
            fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
                                   true /*strokeAndFill*/);

            fSkPaint.setFakeBoldText(false);
        }

        bool canUseRawPaths;
        if (!fStroke.isDashed() && (otherBackendsWillDrawAsPaths ||
                                    kMaxPerformance_RenderMode == renderMode)) {
            // We can draw the glyphs from canonically sized paths.
            fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
            fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();

            // Compensate for the glyphs being scaled by fTextRatio.
            if (!fStroke.isFillStyle()) {
                fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
                                       SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
            }

            fSkPaint.setLinearText(true);
            fSkPaint.setLCDRenderText(false);
            fSkPaint.setAutohinted(false);
            fSkPaint.setHinting(SkPaint::kNo_Hinting);
            fSkPaint.setSubpixelText(true);
            fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));

            canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
                             0 == fSkPaint.getTextSkewX() &&
                             !fSkPaint.isFakeBoldText() &&
                             !fSkPaint.isVerticalText();
        } else {
            fTextRatio = fTextInverseRatio = 1.0f;
            canUseRawPaths = false;
        }

        SkMatrix textMatrix;
        // Glyphs loaded by GPU path rendering have an inverted y-direction.
        textMatrix.setScale(fTextRatio, -fTextRatio);
        fViewMatrix.preConcat(textMatrix);
        fLocalMatrix = textMatrix;

        fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, true /*ignoreGamma*/);
        fGlyphs = canUseRawPaths ?
                      get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) :
                      get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
                                    &fGlyphCache->getDescriptor(), fStroke);
    }

    fStateRestore.set(&fPipelineBuilder);

    fPipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);

    GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
                                 kZero_StencilOp,
                                 kZero_StencilOp,
                                 kNotEqual_StencilFunc,
                                 0xffff,
                                 0x0000,
                                 0xffff);

    *fPipelineBuilder.stencil() = kStencilPass;

    SkASSERT(0 == fQueuedGlyphCount);
    SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx);
}
Beispiel #15
0
static void generateMask(const SkMask& mask, const SkPath& path) {
    SkBitmap::Config config;
    SkPaint     paint;

    int srcW = mask.fBounds.width();
    int srcH = mask.fBounds.height();
    int dstW = srcW;
    int dstH = srcH;
    int dstRB = mask.fRowBytes;

    SkMatrix matrix;
    matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
                        -SkIntToScalar(mask.fBounds.fTop));

    if (SkMask::kBW_Format == mask.fFormat) {
        config = SkBitmap::kA1_Config;
        paint.setAntiAlias(false);
    } else {
        config = SkBitmap::kA8_Config;
        paint.setAntiAlias(true);
        switch (mask.fFormat) {
            case SkMask::kA8_Format:
                break;
            case SkMask::kLCD16_Format:
            case SkMask::kLCD32_Format:
                // TODO: trigger off LCD orientation
                dstW *= 3;
                matrix.postScale(SkIntToScalar(3), SK_Scalar1);
                dstRB = 0;  // signals we need a copy
                break;
            default:
                SkDEBUGFAIL("unexpected mask format");
        }
    }

    SkRasterClip clip;
    clip.setRect(SkIRect::MakeWH(dstW, dstH));

    SkBitmap bm;
    bm.setConfig(config, dstW, dstH, dstRB);

    if (0 == dstRB) {
        bm.allocPixels();
        bm.lockPixels();
    } else {
        bm.setPixels(mask.fImage);
    }
    sk_bzero(bm.getPixels(), bm.getSafeSize());
    
    SkDraw  draw;
    sk_bzero(&draw, sizeof(draw));
    draw.fRC    = &clip;
    draw.fClip  = &clip.bwRgn();
    draw.fMatrix = &matrix;
    draw.fBitmap = &bm;
    draw.drawPath(path, paint);
    
    if (0 == dstRB) {
        switch (mask.fFormat) {
            case SkMask::kLCD16_Format:
                pack3xHToLCD16(bm, mask);
                break;
            case SkMask::kLCD32_Format:
                pack3xHToLCD32(bm, mask);
                break;
            default:
                SkDEBUGFAIL("bad format for copyback");
        }
    }
}
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) {
    fState.get()->fImage.lockPixels();

    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
    finalMatrix.preConcat(fState.get()->fShaderTransform);
    SkRect surfaceBBox;
    surfaceBBox.set(fState.get()->fBBox);
    transformBBox(finalMatrix, &surfaceBBox);

    SkMatrix unflip;
    unflip.setTranslate(0, SkScalarRound(surfaceBBox.height()));
    unflip.preScale(SK_Scalar1, -SK_Scalar1);
    SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()),
                                 SkScalarRound(surfaceBBox.height()));
    SkPDFDevice pattern(size, size, unflip);
    SkCanvas canvas(&pattern);
    canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop);
    finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop);

    const SkBitmap* image = &fState.get()->fImage;
    int width = image->width();
    int height = image->height();
    SkShader::TileMode tileModes[2];
    tileModes[0] = fState.get()->fImageTileModes[0];
    tileModes[1] = fState.get()->fImageTileModes[1];

    canvas.drawBitmap(*image, 0, 0);
    SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop,
                                          width, height);

    // Tiling is implied.  First we handle mirroring.
    if (tileModes[0] == SkShader::kMirror_TileMode) {
        SkMatrix xMirror;
        xMirror.setScale(-1, 1);
        xMirror.postTranslate(2 * width, 0);
        canvas.drawBitmapMatrix(*image, xMirror);
        patternBBox.fRight += width;
    }
    if (tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix yMirror;
        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
        yMirror.postTranslate(0, 2 * height);
        canvas.drawBitmapMatrix(*image, yMirror);
        patternBBox.fBottom += height;
    }
    if (tileModes[0] == SkShader::kMirror_TileMode &&
            tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix mirror;
        mirror.setScale(-1, -1);
        mirror.postTranslate(2 * width, 2 * height);
        canvas.drawBitmapMatrix(*image, mirror);
    }

    // Then handle Clamping, which requires expanding the pattern canvas to
    // cover the entire surfaceBBox.

    // If both x and y are in clamp mode, we start by filling in the corners.
    // (Which are just a rectangles of the corner colors.)
    if (tileModes[0] == SkShader::kClamp_TileMode &&
            tileModes[1] == SkShader::kClamp_TileMode) {
        SkPaint paint;
        SkRect rect;
        rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(width - 1, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight,
                                surfaceBBox.fBottom);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(width - 1, height - 1));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0,
                                surfaceBBox.fBottom);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, height - 1));
            canvas.drawRect(rect, paint);
        }
    }

    // Then expand the left, right, top, then bottom.
    if (tileModes[0] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, height);
        if (surfaceBBox.fLeft < 0) {
            SkBitmap left;
            SkAssertResult(image->extractSubset(&left, subset));

            SkMatrix leftMatrix;
            leftMatrix.setScale(-surfaceBBox.fLeft, 1);
            leftMatrix.postTranslate(surfaceBBox.fLeft, 0);
            canvas.drawBitmapMatrix(left, leftMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                leftMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(left, leftMatrix);
            }
            patternBBox.fLeft = 0;
        }

        if (surfaceBBox.fRight > width) {
            SkBitmap right;
            subset.offset(width - 1, 0);
            SkAssertResult(image->extractSubset(&right, subset));

            SkMatrix rightMatrix;
            rightMatrix.setScale(surfaceBBox.fRight - width, 1);
            rightMatrix.postTranslate(width, 0);
            canvas.drawBitmapMatrix(right, rightMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                rightMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(right, rightMatrix);
            }
            patternBBox.fRight = surfaceBBox.width();
        }
    }

    if (tileModes[1] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1);
        if (surfaceBBox.fTop < 0) {
            SkBitmap top;
            SkAssertResult(image->extractSubset(&top, subset));

            SkMatrix topMatrix;
            topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop);
            topMatrix.postTranslate(0, surfaceBBox.fTop);
            canvas.drawBitmapMatrix(top, topMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                topMatrix.postScale(-1, 1);
                topMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(top, topMatrix);
            }
            patternBBox.fTop = 0;
        }

        if (surfaceBBox.fBottom > height) {
            SkBitmap bottom;
            subset.offset(0, height - 1);
            SkAssertResult(image->extractSubset(&bottom, subset));

            SkMatrix bottomMatrix;
            bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height);
            bottomMatrix.postTranslate(0, height);
            canvas.drawBitmapMatrix(bottom, bottomMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                bottomMatrix.postScale(-1, 1);
                bottomMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(bottom, bottomMatrix);
            }
            patternBBox.fBottom = surfaceBBox.height();
        }
    }

    SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray;
    patternBBoxArray->unref();  // SkRefPtr and new both took a reference.
    patternBBoxArray->reserve(4);
    patternBBoxArray->appendScalar(patternBBox.fLeft);
    patternBBoxArray->appendScalar(patternBBox.fTop);
    patternBBoxArray->appendScalar(patternBBox.fRight);
    patternBBoxArray->appendScalar(patternBBox.fBottom);

    // Put the canvas into the pattern stream (fContent).
    SkRefPtr<SkStream> content = pattern.content();
    content->unref();  // SkRefPtr and content() both took a reference.
    pattern.getResources(&fResources);

    setData(content.get());
    insertName("Type", "Pattern");
    insertInt("PatternType", 1);
    insertInt("PaintType", 1);
    insertInt("TilingType", 1);
    insert("BBox", patternBBoxArray.get());
    insertScalar("XStep", patternBBox.width());
    insertScalar("YStep", patternBBox.height());
    insert("Resources", pattern.getResourceDict());
    insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();

    fState.get()->fImage.unlockPixels();
}
Beispiel #17
0
static void generateMask(const SkMask& mask, const SkPath& path,
                         const SkMaskGamma::PreBlend& maskPreBlend) {
    SkBitmap::Config config;
    SkPaint     paint;

    int srcW = mask.fBounds.width();
    int srcH = mask.fBounds.height();
    int dstW = srcW;
    int dstH = srcH;
    int dstRB = mask.fRowBytes;

    SkMatrix matrix;
    matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
                        -SkIntToScalar(mask.fBounds.fTop));

    if (SkMask::kBW_Format == mask.fFormat) {
        config = SkBitmap::kA1_Config;
        paint.setAntiAlias(false);
    } else {
        config = SkBitmap::kA8_Config;
        paint.setAntiAlias(true);
        switch (mask.fFormat) {
            case SkMask::kA8_Format:
                break;
            case SkMask::kLCD16_Format:
            case SkMask::kLCD32_Format:
                // TODO: trigger off LCD orientation
                dstW *= 3;
                matrix.postScale(SkIntToScalar(3), SK_Scalar1);
                dstRB = 0;  // signals we need a copy
                break;
            default:
                SkDEBUGFAIL("unexpected mask format");
        }
    }

    SkRasterClip clip;
    clip.setRect(SkIRect::MakeWH(dstW, dstH));

    SkBitmap bm;
    bm.setConfig(config, dstW, dstH, dstRB);

    if (0 == dstRB) {
        if (!bm.allocPixels()) {
            // can't allocate offscreen, so empty the mask and return
            sk_bzero(mask.fImage, mask.computeImageSize());
            return;
        }
        bm.lockPixels();
    } else {
        bm.setPixels(mask.fImage);
    }
    sk_bzero(bm.getPixels(), bm.getSafeSize());

    SkDraw  draw;
    draw.fRC    = &clip;
    draw.fClip  = &clip.bwRgn();
    draw.fMatrix = &matrix;
    draw.fBitmap = &bm;
    draw.drawPath(path, paint);

    switch (mask.fFormat) {
        case SkMask::kA8_Format:
            if (maskPreBlend.isApplicable()) {
                applyLUTToA8Mask(mask, maskPreBlend.fG);
            }
            break;
        case SkMask::kLCD16_Format:
            if (maskPreBlend.isApplicable()) {
                pack3xHToLCD16<true>(bm, mask, maskPreBlend);
            } else {
                pack3xHToLCD16<false>(bm, mask, maskPreBlend);
            }
            break;
        case SkMask::kLCD32_Format:
            if (maskPreBlend.isApplicable()) {
                pack3xHToLCD32<true>(bm, mask, maskPreBlend);
            } else {
                pack3xHToLCD32<false>(bm, mask, maskPreBlend);
            }
            break;
        default:
            break;
    }
}
Beispiel #18
0
static void test_matrix_decomposition(skiatest::Reporter* reporter) {
    SkMatrix mat;
    SkPoint rotation1, scale, rotation2;

    const float kRotation0 = 15.5f;
    const float kRotation1 = -50.f;
    const float kScale0 = 5000.f;
    const float kScale1 = 0.001f;

    // identity
    mat.reset();
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
    // make sure it doesn't crash if we pass in NULLs
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL));

    // rotation only
    mat.setRotate(kRotation0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // uniform scale only
    mat.setScale(kScale0, kScale0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // anisotropic scale only
    mat.setScale(kScale1, kScale0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // rotation then uniform scale
    mat.setRotate(kRotation1);
    mat.postScale(kScale0, kScale0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // uniform scale then rotation
    mat.setScale(kScale0, kScale0);
    mat.postRotate(kRotation1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // rotation then uniform scale+reflection
    mat.setRotate(kRotation0);
    mat.postScale(kScale1, -kScale1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // uniform scale+reflection, then rotate
    mat.setScale(kScale0, -kScale0);
    mat.postRotate(kRotation1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // rotation then anisotropic scale
    mat.setRotate(kRotation1);
    mat.postScale(kScale1, kScale0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // rotation then anisotropic scale
    mat.setRotate(90);
    mat.postScale(kScale1, kScale0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // anisotropic scale then rotation
    mat.setScale(kScale1, kScale0);
    mat.postRotate(kRotation0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // anisotropic scale then rotation
    mat.setScale(kScale1, kScale0);
    mat.postRotate(90);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // rotation, uniform scale, then different rotation
    mat.setRotate(kRotation1);
    mat.postScale(kScale0, kScale0);
    mat.postRotate(kRotation0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // rotation, anisotropic scale, then different rotation
    mat.setRotate(kRotation0);
    mat.postScale(kScale1, kScale0);
    mat.postRotate(kRotation1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // rotation, anisotropic scale + reflection, then different rotation
    mat.setRotate(kRotation0);
    mat.postScale(-kScale1, kScale0);
    mat.postRotate(kRotation1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // try some random matrices
    SkRandom rand;
    for (int m = 0; m < 1000; ++m) {
        SkScalar rot0 = rand.nextRangeF(-180, 180);
        SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
        SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
        SkScalar rot1 = rand.nextRangeF(-180, 180);
        mat.setRotate(rot0);
        mat.postScale(sx, sy);
        mat.postRotate(rot1);

        if (SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2)) {
            REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));
        } else {
            // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
            SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
                               mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
            REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
        }
    }

    // translation shouldn't affect this
    mat.postTranslate(-1000.f, 1000.f);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // perspective shouldn't affect this
    mat[SkMatrix::kMPersp0] = 12.f;
    mat[SkMatrix::kMPersp1] = 4.f;
    mat[SkMatrix::kMPersp2] = 1872.f;
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    REPORTER_ASSERT(reporter, check_matrix_recomposition(mat, rotation1, scale, rotation2));

    // degenerate matrices
    // mostly zero entries
    mat.reset();
    mat[SkMatrix::kMScaleX] = 0.f;
    REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    mat.reset();
    mat[SkMatrix::kMScaleY] = 0.f;
    REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
    mat.reset();
    // linearly dependent entries
    mat[SkMatrix::kMScaleX] = 1.f;
    mat[SkMatrix::kMSkewX] = 2.f;
    mat[SkMatrix::kMSkewY] = 4.f;
    mat[SkMatrix::kMScaleY] = 8.f;
    REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation1, &scale, &rotation2));
}
Beispiel #19
0
    DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
        if (!fAnim) {
            *errorMsg = "No animation.";
            return DrawResult::kFail;
        }
        SkMatrix44  camera,
                    perspective,
                    mv;
        SkMatrix    viewport;

        {
            float w = this->width();
            float h = this->height();
            float s = std::min(w, h);
            viewport.setTranslate(1, -1);
            viewport.postScale(s/2, -s/2);

            draw_viewport(canvas, viewport);
        }

        Sk3Perspective(&perspective, fNear, fFar, fAngle);
        Sk3LookAt(&camera, fEye, fCOA, fUp);
        mv.postConcat(camera);
        mv.postConcat(perspective);
        SkPoint pts[8];
        Sk3MapPts(pts, mv, fP3, 8);
        viewport.mapPoints(pts, 8);

        SkPaint paint;
        paint.setStyle(SkPaint::kStroke_Style);
        SkFont font;
        font.setEdging(SkFont::Edging::kAlias);

        SkPath cube;

        cube.moveTo(pts[0]);
        cube.lineTo(pts[2]);
        cube.lineTo(pts[6]);
        cube.lineTo(pts[4]);
        cube.close();

        cube.moveTo(pts[1]);
        cube.lineTo(pts[3]);
        cube.lineTo(pts[7]);
        cube.lineTo(pts[5]);
        cube.close();

        cube.moveTo(pts[0]);    cube.lineTo(pts[1]);
        cube.moveTo(pts[2]);    cube.lineTo(pts[3]);
        cube.moveTo(pts[4]);    cube.lineTo(pts[5]);
        cube.moveTo(pts[6]);    cube.lineTo(pts[7]);

        canvas->drawPath(cube, paint);

        {
            SkPoint3 src[4] = {
                { 0, 0, 0 }, { 2, 0, 0 }, { 0, 2, 0 }, { 0, 0, 2 },
            };
            SkPoint dst[4];
            mv.setConcat(perspective, camera);
            Sk3MapPts(dst, mv, src, 4);
            viewport.mapPoints(dst, 4);
            const char* str[3] = { "X", "Y", "Z" };
            for (int i = 1; i <= 3; ++i) {
                canvas->drawLine(dst[0], dst[i], paint);
            }

            for (int i = 0; i < 3; ++i) {
                canvas->drawString(str[i], dst[i + 1].fX, dst[i + 1].fY, font, paint);
            }
        }

        fAnim->seek(fAnimT);
        draw_skia(canvas, mv, viewport, fAnim.get());
        return DrawResult::kOk;
    }
Beispiel #20
0
    /* The srcType argument indicates what to draw for the source part. Skia
     * uses the implied shape of the drawing command and these modes
     * demonstrate that.
     */
    void draw_mode(SkCanvas* canvas, SkXfermode* mode, SrcType srcType,
                   SkScalar x, SkScalar y) {
        SkPaint p;
        SkMatrix m;
        bool restoreNeeded = false;
        m.setTranslate(x, y);

        canvas->drawBitmap(fSrcB, x, y, &p);
        p.setXfermode(mode);
        switch (srcType) {
            case kSmallTransparentImage_SrcType: {
                m.postScale(SK_ScalarHalf, SK_ScalarHalf, x, y);

                SkAutoCanvasRestore acr(canvas, true);
                canvas->concat(m);
                canvas->drawBitmap(fTransparent, 0, 0, &p);
                break;
            }
            case kQuarterClearInLayer_SrcType: {
                SkRect bounds = SkRect::MakeXYWH(x, y, SkIntToScalar(W),
                                                 SkIntToScalar(H));
                canvas->saveLayer(&bounds, &p);
                restoreNeeded = true;
                p.setXfermodeMode(SkXfermode::kSrcOver_Mode);
                // Fall through.
            }
            case kQuarterClear_SrcType: {
                SkScalar halfW = SkIntToScalar(W) / 2;
                SkScalar halfH = SkIntToScalar(H) / 2;
                p.setColor(sk_tool_utils::color_to_565(0xFF66AAFF));
                SkRect r = SkRect::MakeXYWH(x + halfW, y, halfW,
                                            SkIntToScalar(H));
                canvas->drawRect(r, p);
                p.setColor(sk_tool_utils::color_to_565(0xFFAA66FF));
                r = SkRect::MakeXYWH(x, y + halfH, SkIntToScalar(W), halfH);
                canvas->drawRect(r, p);
                break;
            }
            case kRectangleWithMask_SrcType: {
                canvas->save();
                restoreNeeded = true;
                SkScalar w = SkIntToScalar(W);
                SkScalar h = SkIntToScalar(H);
                SkRect r = SkRect::MakeXYWH(x, y + h / 4, w, h * 23 / 60);
                canvas->clipRect(r);
                // Fall through.
            }
            case kRectangle_SrcType: {
                SkScalar w = SkIntToScalar(W);
                SkScalar h = SkIntToScalar(H);
                SkRect r = SkRect::MakeXYWH(x + w / 3, y + h / 3,
                                            w * 37 / 60, h * 37 / 60);
                p.setColor(sk_tool_utils::color_to_565(0xFF66AAFF));
                canvas->drawRect(r, p);
                break;
            }
            case kSmallRectangleImageWithAlpha_SrcType:
                m.postScale(SK_ScalarHalf, SK_ScalarHalf, x, y);
                // Fall through.
            case kRectangleImageWithAlpha_SrcType:
                p.setAlpha(0x88);
                // Fall through.
            case kRectangleImage_SrcType: {
                SkAutoCanvasRestore acr(canvas, true);
                canvas->concat(m);
                canvas->drawBitmap(fDstB, 0, 0, &p);
                break;
            }
            default:
                break;
        }

        if (restoreNeeded) {
            canvas->restore();
        }
    }
Beispiel #21
0
static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
    SkMatrix mat;

    // identity
    mat.setIdentity();
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // translation only
    mat.reset();
    mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with same size
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with one negative
    mat.reset();
    mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with different size
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // scale with same size at a pivot point
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
                 SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with different size at a pivot point
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
                 SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with same size
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with different size
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with same size at a pivot point
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
                SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with different size at a pivot point
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
                SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // perspective x
    mat.reset();
    mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // perspective y
    mat.reset();
    mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // rotate
    for (int angle = 0; angle < 360; ++angle) {
        mat.reset();
        mat.setRotate(SkIntToScalar(angle));
#ifndef SK_CPU_ARM64
        REPORTER_ASSERT(reporter, mat.isSimilarity());
#else
        // 64-bit ARM devices built with -O2 and -ffp-contract=fast have a loss
        // of precision and require that we have a higher tolerance
        REPORTER_ASSERT(reporter, mat.isSimilarity(SK_ScalarNearlyZero + 0.00010113f));
#endif
    }

    // see if there are any accumulated precision issues
    mat.reset();
    for (int i = 1; i < 360; i++) {
        mat.postRotate(SkIntToScalar(1));
    }
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // rotate + translate
    mat.reset();
    mat.setRotate(SkIntToScalar(30));
    mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // rotate + uniform scale
    mat.reset();
    mat.setRotate(SkIntToScalar(30));
    mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // rotate + non-uniform scale
    mat.reset();
    mat.setRotate(SkIntToScalar(30));
    mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // all zero
    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // all zero except perspective
    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // scales zero, only skews
    mat.setAll(0, SK_Scalar1, 0,
               SK_Scalar1, 0, 0,
               0, 0, SkMatrix::I()[8]);
    REPORTER_ASSERT(reporter, mat.isSimilarity());
}
void SkPDFDevice::updateGSFromPaint(const SkPaint& paint, bool forText) {
    SkASSERT(paint.getPathEffect() == NULL);

    NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
    NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);

    SkPaint newPaint = paint;

    // PDF treats a shader as a color, so we only set one or the other.
    SkRefPtr<SkPDFShader> pdfShader;
    const SkShader* shader = newPaint.getShader();
    if (shader) {
        // PDF positions patterns relative to the initial transform, so
        // we need to apply the current transform to the shader parameters.
        SkMatrix transform = fGraphicStack[fGraphicStackIndex].fTransform;
        if (fFlipOrigin == kFlip_OriginTransform) {
            transform.postScale(1, -1);
            transform.postTranslate(0, fHeight);
        }

        // PDF doesn't support kClamp_TileMode, so we simulate it by making
        // a pattern the size of the drawing service.
        SkIRect bounds = fGraphicStack[fGraphicStackIndex].fClip.getBounds();
        pdfShader = SkPDFShader::getPDFShader(*shader, transform, bounds);
        SkSafeUnref(pdfShader.get());  // getShader and SkRefPtr both took a ref

        // A color shader is treated as an invalid shader so we don't have
        // to set a shader just for a color.
        if (pdfShader.get() == NULL) {
            newPaint.setColor(0);

            // Check for a color shader.
            SkShader::GradientInfo gradientInfo;
            SkColor gradientColor;
            gradientInfo.fColors = &gradientColor;
            gradientInfo.fColorOffsets = NULL;
            gradientInfo.fColorCount = 1;
            if (shader->asAGradient(&gradientInfo) ==
                    SkShader::kColor_GradientType) {
                newPaint.setColor(gradientColor);
            }
        }
    }

    if (pdfShader) {
        // pdfShader has been canonicalized so we can directly compare
        // pointers.
        if (fGraphicStack[fGraphicStackIndex].fShader != pdfShader.get()) {
            int resourceIndex = fShaderResources.find(pdfShader.get());
            if (resourceIndex < 0) {
                resourceIndex = fShaderResources.count();
                fShaderResources.push(pdfShader.get());
                pdfShader->ref();
            }
            fContent.writeText("/Pattern CS /Pattern cs /P");
            fContent.writeDecAsText(resourceIndex);
            fContent.writeText(" SCN /P");
            fContent.writeDecAsText(resourceIndex);
            fContent.writeText(" scn\n");
            fGraphicStack[fGraphicStackIndex].fShader = pdfShader.get();
        }
    } else {
        SkColor newColor = newPaint.getColor();
        newColor = SkColorSetA(newColor, 0xFF);
        if (fGraphicStack[fGraphicStackIndex].fShader ||
                fGraphicStack[fGraphicStackIndex].fColor != newColor) {
            emitPDFColor(newColor, &fContent);
            fContent.writeText("RG ");
            emitPDFColor(newColor, &fContent);
            fContent.writeText("rg\n");
            fGraphicStack[fGraphicStackIndex].fColor = newColor;
            fGraphicStack[fGraphicStackIndex].fShader = NULL;
        }
    }

    SkRefPtr<SkPDFGraphicState> newGraphicState =
        SkPDFGraphicState::getGraphicStateForPaint(newPaint);
    newGraphicState->unref();  // getGraphicState and SkRefPtr both took a ref.
    // newGraphicState has been canonicalized so we can directly compare
    // pointers.
    if (fGraphicStack[fGraphicStackIndex].fGraphicState !=
            newGraphicState.get()) {
        int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
        if (resourceIndex < 0) {
            resourceIndex = fGraphicStateResources.count();
            fGraphicStateResources.push(newGraphicState.get());
            newGraphicState->ref();
        }
        fContent.writeText("/G");
        fContent.writeDecAsText(resourceIndex);
        fContent.writeText(" gs\n");
        fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState.get();
    }

    if (forText) {
        if (fGraphicStack[fGraphicStackIndex].fTextScaleX !=
                newPaint.getTextScaleX()) {
            SkScalar scale = newPaint.getTextScaleX();
            SkScalar pdfScale = SkScalarMul(scale, SkIntToScalar(100));
            SkPDFScalar::Append(pdfScale, &fContent);
            fContent.writeText(" Tz\n");
            fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
        }

        if (fGraphicStack[fGraphicStackIndex].fTextFill !=
                newPaint.getStyle()) {
            SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
            SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
                              enum_must_match_value);
            SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
                              enum_must_match_value);
            fContent.writeDecAsText(newPaint.getStyle());
            fContent.writeText(" Tr\n");
            fGraphicStack[fGraphicStackIndex].fTextFill = newPaint.getStyle();
        }
    }
}
static void generateMask(const SkMask& mask, const SkPath& path,
                         const SkMaskGamma::PreBlend& maskPreBlend) {
    SkPaint paint;

    int srcW = mask.fBounds.width();
    int srcH = mask.fBounds.height();
    int dstW = srcW;
    int dstH = srcH;
    int dstRB = mask.fRowBytes;

    SkMatrix matrix;
    matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
                        -SkIntToScalar(mask.fBounds.fTop));

    paint.setAntiAlias(SkMask::kBW_Format != mask.fFormat);
    switch (mask.fFormat) {
        case SkMask::kBW_Format:
            dstRB = 0;  // signals we need a copy
            break;
        case SkMask::kA8_Format:
            break;
        case SkMask::kLCD16_Format:
            // TODO: trigger off LCD orientation
            dstW = 4*dstW - 8;
            matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft + 1),
                                -SkIntToScalar(mask.fBounds.fTop));
            matrix.postScale(SkIntToScalar(4), SK_Scalar1);
            dstRB = 0;  // signals we need a copy
            break;
        default:
            SkDEBUGFAIL("unexpected mask format");
    }

    SkRasterClip clip;
    clip.setRect(SkIRect::MakeWH(dstW, dstH));

    const SkImageInfo info = SkImageInfo::MakeA8(dstW, dstH);
    SkAutoPixmapStorage dst;

    if (0 == dstRB) {
        if (!dst.tryAlloc(info)) {
            // can't allocate offscreen, so empty the mask and return
            sk_bzero(mask.fImage, mask.computeImageSize());
            return;
        }
    } else {
        dst.reset(info, mask.fImage, dstRB);
    }
    sk_bzero(dst.writable_addr(), dst.getSafeSize());

    SkDraw  draw;
    draw.fDst   = dst;
    draw.fRC    = &clip;
    draw.fClip  = &clip.bwRgn();
    draw.fMatrix = &matrix;
    draw.drawPath(path, paint);

    switch (mask.fFormat) {
        case SkMask::kBW_Format:
            packA8ToA1(mask, dst.addr8(0, 0), dst.rowBytes());
            break;
        case SkMask::kA8_Format:
            if (maskPreBlend.isApplicable()) {
                applyLUTToA8Mask(mask, maskPreBlend.fG);
            }
            break;
        case SkMask::kLCD16_Format:
            if (maskPreBlend.isApplicable()) {
                pack4xHToLCD16<true>(dst, mask, maskPreBlend);
            } else {
                pack4xHToLCD16<false>(dst, mask, maskPreBlend);
            }
            break;
        default:
            break;
    }
}
    bool addPathToAtlas(GrVertexBatch::Target* target,
                        FlushInfo* flushInfo,
                        GrBatchAtlas* atlas,
                        ShapeData* shapeData,
                        const GrShape& shape,
                        bool antiAlias,
                        uint32_t dimension,
                        SkScalar scale) const {
        const SkRect& bounds = shape.bounds();

        // generate bounding rect for bitmap draw
        SkRect scaledBounds = bounds;
        // scale to mip level size
        scaledBounds.fLeft *= scale;
        scaledBounds.fTop *= scale;
        scaledBounds.fRight *= scale;
        scaledBounds.fBottom *= scale;
        // move the origin to an integer boundary (gives better results)
        SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
        SkScalar dy = SkScalarFraction(scaledBounds.fTop);
        scaledBounds.offset(-dx, -dy);
        // get integer boundary
        SkIRect devPathBounds;
        scaledBounds.roundOut(&devPathBounds);
        // pad to allow room for antialiasing
        const int intPad = SkScalarCeilToInt(kAntiAliasPad);
        // pre-move origin (after outset, will be 0,0)
        int width = devPathBounds.width();
        int height = devPathBounds.height();
        devPathBounds.fLeft = intPad;
        devPathBounds.fTop = intPad;
        devPathBounds.fRight = intPad + width;
        devPathBounds.fBottom = intPad + height;
        devPathBounds.outset(intPad, intPad);

        // draw path to bitmap
        SkMatrix drawMatrix;
        drawMatrix.setTranslate(-bounds.left(), -bounds.top());
        drawMatrix.postScale(scale, scale);
        drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);

        // setup bitmap backing
        SkASSERT(devPathBounds.fLeft == 0);
        SkASSERT(devPathBounds.fTop == 0);
        SkAutoPixmapStorage dst;
        if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
                                              devPathBounds.height()))) {
            return false;
        }
        sk_bzero(dst.writable_addr(), dst.getSafeSize());

        // rasterize path
        SkPaint paint;
        paint.setStyle(SkPaint::kFill_Style);
        paint.setAntiAlias(antiAlias);

        SkDraw draw;
        sk_bzero(&draw, sizeof(draw));

        SkRasterClip rasterClip;
        rasterClip.setRect(devPathBounds);
        draw.fRC = &rasterClip;
        draw.fMatrix = &drawMatrix;
        draw.fDst = dst;

        SkPath path;
        shape.asPath(&path);
        draw.drawPathCoverage(path, paint);

        // generate signed distance field
        devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
        width = devPathBounds.width();
        height = devPathBounds.height();
        // TODO We should really generate this directly into the plot somehow
        SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));

        // Generate signed distance field
        SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
                                           (const unsigned char*)dst.addr(),
                                           dst.width(), dst.height(), dst.rowBytes());

        // add to atlas
        SkIPoint16 atlasLocation;
        GrBatchAtlas::AtlasID id;
       if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
            this->flush(target, flushInfo);
            if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
                return false;
            }
        }

        // add to cache
        shapeData->fKey.set(shape, dimension);
        shapeData->fScale = scale;
        shapeData->fID = id;
        // change the scaled rect to match the size of the inset distance field
        scaledBounds.fRight = scaledBounds.fLeft +
            SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
        scaledBounds.fBottom = scaledBounds.fTop +
            SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
        // shift the origin to the correct place relative to the distance field
        // need to also restore the fractional translation
        scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
                            -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
        shapeData->fBounds = scaledBounds;
        // origin we render from is inset from distance field edge
        atlasLocation.fX += SK_DistanceFieldInset;
        atlasLocation.fY += SK_DistanceFieldInset;
        shapeData->fAtlasLocation = atlasLocation;

        fShapeCache->add(shapeData);
        fShapeList->addToTail(shapeData);
#ifdef DF_PATH_TRACKING
        ++g_NumCachedPaths;
#endif
        return true;
    }
static void test_matrix_decomposition(skiatest::Reporter* reporter) {
    SkMatrix mat;
    SkScalar rotation0, scaleX, scaleY, rotation1;

    const float kRotation0 = 15.5f;
    const float kRotation1 = -50.f;
    const float kScale0 = 5000.f;
    const float kScale1 = 0.001f;

    // identity
    mat.reset();
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, SK_Scalar1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, SK_Scalar1));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
    // make sure it doesn't crash if we pass in NULLs
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL, NULL));

    // rotation only
    mat.setRotate(kRotation0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation0)));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, SK_Scalar1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, SK_Scalar1));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));

    // uniform scale only
    mat.setScale(kScale0, kScale0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));

    // anisotropic scale only
    mat.setScale(kScale1, kScale0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));

    // rotation then uniform scale
    mat.setRotate(kRotation1);
    mat.postScale(kScale0, kScale0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation1)));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));

    // uniform scale then rotation
    mat.setScale(kScale0, kScale0);
    mat.postRotate(kRotation1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation1)));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));

    // rotation then uniform scale+reflection
    mat.setRotate(kRotation0);
    mat.postScale(kScale1, -kScale1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation0)));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, -kScale1));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));

    // uniform scale+reflection, then rotate
    mat.setScale(kScale0, -kScale0);
    mat.postRotate(kRotation1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(-kRotation1)));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, -kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));

    // rotation then anisotropic scale
    mat.setRotate(kRotation1);
    mat.postScale(kScale1, kScale0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation1)));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));

    // anisotropic scale then rotation
    mat.setScale(kScale1, kScale0);
    mat.postRotate(kRotation0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation1, SkDegreesToRadians(kRotation0)));

    // rotation, uniform scale, then different rotation
    mat.setRotate(kRotation1);
    mat.postScale(kScale0, kScale0);
    mat.postRotate(kRotation0);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0,
                                                  SkDegreesToRadians(kRotation0 + kRotation1)));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
    REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));

    // rotation, anisotropic scale, then different rotation
    mat.setRotate(kRotation0);
    mat.postScale(kScale1, kScale0);
    mat.postRotate(kRotation1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    // Because of the shear/skew we won't get the same results, so we need to multiply it out.
    // Generating the matrices requires doing a radian-to-degree calculation, then degree-to-radian
    // calculation (in setRotate()), which adds error, so this just computes the matrix elements
    // directly.
    SkScalar c0;
    SkScalar s0 = SkScalarSinCos(rotation0, &c0);
    SkScalar c1;
    SkScalar s1 = SkScalarSinCos(rotation1, &c1);
    // We do a relative check here because large scale factors cause problems with an absolute check
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
                                                           scaleX*c0*c1 - scaleY*s0*s1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
                                                           -scaleX*s0*c1 - scaleY*c0*s1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
                                                           scaleX*c0*s1 + scaleY*s0*c1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
                                                           -scaleX*s0*s1 + scaleY*c0*c1));

    // try some random matrices
    SkMWCRandom rand;
    for (int m = 0; m < 1000; ++m) {
        SkScalar rot0 = rand.nextRangeF(-SK_ScalarPI, SK_ScalarPI);
        SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
        SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
        SkScalar rot1 = rand.nextRangeF(-SK_ScalarPI, SK_ScalarPI);
        mat.setRotate(rot0);
        mat.postScale(sx, sy);
        mat.postRotate(rot1);

        if (SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)) {
            SkScalar c0;
            SkScalar s0 = SkScalarSinCos(rotation0, &c0);
            SkScalar c1;
            SkScalar s1 = SkScalarSinCos(rotation1, &c1);
            REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
                                                                   scaleX*c0*c1 - scaleY*s0*s1));
            REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
                                                                   -scaleX*s0*c1 - scaleY*c0*s1));
            REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
                                                                   scaleX*c0*s1 + scaleY*s0*c1));
            REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
                                                                   -scaleX*s0*s1 + scaleY*c0*c1));
        } else {
            // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero
            SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
                               mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
            REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
        }
    }

    // translation shouldn't affect this
    mat.postTranslate(-1000.f, 1000.f);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    s0 = SkScalarSinCos(rotation0, &c0);
    s1 = SkScalarSinCos(rotation1, &c1);
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
                                                           scaleX*c0*c1 - scaleY*s0*s1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
                                                           -scaleX*s0*c1 - scaleY*c0*s1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
                                                           scaleX*c0*s1 + scaleY*s0*c1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
                                                           -scaleX*s0*s1 + scaleY*c0*c1));

    // perspective shouldn't affect this
    mat[SkMatrix::kMPersp0] = 12.f;
    mat[SkMatrix::kMPersp1] = 4.f;
    mat[SkMatrix::kMPersp2] = 1872.f;
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    s0 = SkScalarSinCos(rotation0, &c0);
    s1 = SkScalarSinCos(rotation1, &c1);
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
                                                           scaleX*c0*c1 - scaleY*s0*s1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
                                                           -scaleX*s0*c1 - scaleY*c0*s1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
                                                           scaleX*c0*s1 + scaleY*s0*c1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
                                                           -scaleX*s0*s1 + scaleY*c0*c1));

    // rotation, anisotropic scale + reflection, then different rotation
    mat.setRotate(kRotation0);
    mat.postScale(-kScale1, kScale0);
    mat.postRotate(kRotation1);
    REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    s0 = SkScalarSinCos(rotation0, &c0);
    s1 = SkScalarSinCos(rotation1, &c1);
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX],
                                                           scaleX*c0*c1 - scaleY*s0*s1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX],
                                                           -scaleX*s0*c1 - scaleY*c0*s1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY],
                                                           scaleX*c0*s1 + scaleY*s0*c1));
    REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY],
                                                           -scaleX*s0*s1 + scaleY*c0*c1));

    // degenerate matrices
    // mostly zero entries
    mat.reset();
    mat[SkMatrix::kMScaleX] = 0.f;
    REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    mat.reset();
    mat[SkMatrix::kMScaleY] = 0.f;
    REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
    mat.reset();
    // linearly dependent entries
    mat[SkMatrix::kMScaleX] = 1.f;
    mat[SkMatrix::kMSkewX] = 2.f;
    mat[SkMatrix::kMSkewY] = 4.f;
    mat[SkMatrix::kMScaleY] = 8.f;
    REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1));
}
Beispiel #26
0
void SkGLDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
                              int vertexCount, const SkPoint vertices[],
                              const SkPoint texs[], const SkColor colors[],
                              SkXfermode* xmode,
                              const uint16_t indices[], int indexCount,
                              const SkPaint& paint) {

    if (false) {
        SkRect bounds;
        SkIRect ibounds;
        
        bounds.set(vertices, vertexCount);
        bounds.round(&ibounds);
        
        SkDebugf("---- drawverts: %d pts, texs=%d colors=%d indices=%d bounds [%d %d]\n",
                 vertexCount, texs!=0, colors!=0, indexCount, ibounds.width(), ibounds.height());
    }
    
    SkGLClipIter* iter = this->updateMatrixClip();
    
    SkGL::SetPaint(paint);
    
    const SkGLVertex* glVerts;
    const SkGLVertex* glTexs = NULL;
    
#if GLSCALAR_IS_SCALAR
    glVerts = (const SkGLVertex*)vertices;
#else
    SkAutoSTMalloc<32, SkGLVertex> storage(vertexCount);
    storage.get()->setPoints(vertices, vertexCount);
    glVerts = storage.get();
#endif
    
    uint8_t* colorArray = NULL;
    if (colors) {
        colorArray = (uint8_t*)sk_malloc_throw(vertexCount*4);
        SkGL::SetRGBA(colorArray, colors, vertexCount);
    }
    SkAutoFree afca(colorArray);
    
    SkGLVertex* texArray = NULL;
    TexCache* cache = NULL;

    if (texs && paint.getShader()) {
        SkShader* shader = paint.getShader();
        
        //        if (!shader->setContext(this->accessBitmap(), paint, *draw.fMatrix)) {
        if (!shader->setContext(*draw.fBitmap, paint, *draw.fMatrix)) {
            goto DONE;
        }
        
        SkBitmap bitmap;
        SkMatrix matrix;
        SkShader::TileMode tileModes[2];
        if (shader->asABitmap(&bitmap, &matrix, tileModes)) {
            SkPoint max;
            GLuint name;
            cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
            if (NULL == cache) {
                return;
            }

            matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
            glMatrixMode(GL_TEXTURE);
            SkGL::LoadMatrix(matrix);
            glMatrixMode(GL_MODELVIEW);
            
#if GLSCALAR_IS_SCALAR
            glTexs = (const SkGLVertex*)texs;
#else
            texArray = (SkGLVertex*)sk_malloc_throw(vertexCount * sizeof(SkGLVertex));
            texArray->setPoints(texs, vertexCount);
            glTexs = texArray;
#endif
            
            SkGL::SetPaintAlpha(paint);
            SkGL::SetTexParams(paint.isFilterBitmap(),
                               tileModes[0], tileModes[1]);
        }
    }
DONE:
    SkAutoFree aftex(texArray);
    
    SkGL::DrawVertices(indices ? indexCount : vertexCount,
                       gVertexModeToGL[vmode],
                       glVerts, glTexs, colorArray, indices, iter);
    
    if (cache) {
        SkGLDevice::UnlockTexCache(cache);
    }
}
Beispiel #27
0
static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
    SkMatrix mat;

    // identity
    mat.setIdentity();
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // translation only
    mat.reset();
    mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with same size
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with one negative
    mat.reset();
    mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with different size
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // scale with same size at a pivot point
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
                 SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // scale with different size at a pivot point
    mat.reset();
    mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
                 SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with same size
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with different size
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with same size at a pivot point
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
                SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // skew with different size at a pivot point
    mat.reset();
    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
                SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // perspective x
    mat.reset();
    mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // perspective y
    mat.reset();
    mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // rotate
    for (int angle = 0; angle < 360; ++angle) {
        mat.reset();
        mat.setRotate(SkIntToScalar(angle));
        REPORTER_ASSERT(reporter, mat.isSimilarity());
    }

    // see if there are any accumulated precision issues
    mat.reset();
    for (int i = 1; i < 360; i++) {
        mat.postRotate(SkIntToScalar(1));
    }
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // rotate + translate
    mat.reset();
    mat.setRotate(SkIntToScalar(30));
    mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // rotate + uniform scale
    mat.reset();
    mat.setRotate(SkIntToScalar(30));
    mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, mat.isSimilarity());

    // rotate + non-uniform scale
    mat.reset();
    mat.setRotate(SkIntToScalar(30));
    mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // all zero
    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // all zero except perspective
    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
    REPORTER_ASSERT(reporter, !mat.isSimilarity());

    // scales zero, only skews
    mat.setAll(0, SK_Scalar1, 0,
               SK_Scalar1, 0, 0,
               0, 0, SkMatrix::I()[8]);
    REPORTER_ASSERT(reporter, mat.isSimilarity());
}
Beispiel #28
0
SkPDFImageShader* SkPDFImageShader::Create(
        SkPDFCanon* canon,
        SkScalar dpi,
        SkAutoTDelete<SkPDFShader::State>* autoState) {
    const SkPDFShader::State& state = **autoState;

    state.fImage.lockPixels();

    // The image shader pattern cell will be drawn into a separate device
    // in pattern cell space (no scaling on the bitmap, though there may be
    // translations so that all content is in the device, coordinates > 0).

    // Map clip bounds to shader space to ensure the device is large enough
    // to handle fake clamping.
    SkMatrix finalMatrix = state.fCanvasTransform;
    finalMatrix.preConcat(state.fShaderTransform);
    SkRect deviceBounds;
    deviceBounds.set(state.fBBox);
    if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) {
        return NULL;
    }

    const SkBitmap* image = &state.fImage;
    SkRect bitmapBounds;
    image->getBounds(&bitmapBounds);

    // For tiling modes, the bounds should be extended to include the bitmap,
    // otherwise the bitmap gets clipped out and the shader is empty and awful.
    // For clamp modes, we're only interested in the clip region, whether
    // or not the main bitmap is in it.
    SkShader::TileMode tileModes[2];
    tileModes[0] = state.fImageTileModes[0];
    tileModes[1] = state.fImageTileModes[1];
    if (tileModes[0] != SkShader::kClamp_TileMode ||
            tileModes[1] != SkShader::kClamp_TileMode) {
        deviceBounds.join(bitmapBounds);
    }

    SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()),
                                 SkScalarRoundToInt(deviceBounds.height()));
    SkAutoTUnref<SkPDFDevice> patternDevice(
            SkPDFDevice::CreateUnflipped(size, dpi, canon));
    SkCanvas canvas(patternDevice.get());

    SkRect patternBBox;
    image->getBounds(&patternBBox);

    // Translate the canvas so that the bitmap origin is at (0, 0).
    canvas.translate(-deviceBounds.left(), -deviceBounds.top());
    patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
    // Undo the translation in the final matrix
    finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());

    // If the bitmap is out of bounds (i.e. clamp mode where we only see the
    // stretched sides), canvas will clip this out and the extraneous data
    // won't be saved to the PDF.
    canvas.drawBitmap(*image, 0, 0);

    SkScalar width = SkIntToScalar(image->width());
    SkScalar height = SkIntToScalar(image->height());

    // Tiling is implied.  First we handle mirroring.
    if (tileModes[0] == SkShader::kMirror_TileMode) {
        SkMatrix xMirror;
        xMirror.setScale(-1, 1);
        xMirror.postTranslate(2 * width, 0);
        drawBitmapMatrix(&canvas, *image, xMirror);
        patternBBox.fRight += width;
    }
    if (tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix yMirror;
        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
        yMirror.postTranslate(0, 2 * height);
        drawBitmapMatrix(&canvas, *image, yMirror);
        patternBBox.fBottom += height;
    }
    if (tileModes[0] == SkShader::kMirror_TileMode &&
            tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix mirror;
        mirror.setScale(-1, -1);
        mirror.postTranslate(2 * width, 2 * height);
        drawBitmapMatrix(&canvas, *image, mirror);
    }

    // Then handle Clamping, which requires expanding the pattern canvas to
    // cover the entire surfaceBBox.

    // If both x and y are in clamp mode, we start by filling in the corners.
    // (Which are just a rectangles of the corner colors.)
    if (tileModes[0] == SkShader::kClamp_TileMode &&
            tileModes[1] == SkShader::kClamp_TileMode) {
        SkPaint paint;
        SkRect rect;
        rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, deviceBounds.top(),
                                deviceBounds.right(), 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(image->width() - 1, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, height,
                                deviceBounds.right(), deviceBounds.bottom());
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(image->width() - 1,
                                           image->height() - 1));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(deviceBounds.left(), height,
                                0, deviceBounds.bottom());
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, image->height() - 1));
            canvas.drawRect(rect, paint);
        }
    }

    // Then expand the left, right, top, then bottom.
    if (tileModes[0] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height());
        if (deviceBounds.left() < 0) {
            SkBitmap left;
            SkAssertResult(image->extractSubset(&left, subset));

            SkMatrix leftMatrix;
            leftMatrix.setScale(-deviceBounds.left(), 1);
            leftMatrix.postTranslate(deviceBounds.left(), 0);
            drawBitmapMatrix(&canvas, left, leftMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                leftMatrix.postTranslate(0, 2 * height);
                drawBitmapMatrix(&canvas, left, leftMatrix);
            }
            patternBBox.fLeft = 0;
        }

        if (deviceBounds.right() > width) {
            SkBitmap right;
            subset.offset(image->width() - 1, 0);
            SkAssertResult(image->extractSubset(&right, subset));

            SkMatrix rightMatrix;
            rightMatrix.setScale(deviceBounds.right() - width, 1);
            rightMatrix.postTranslate(width, 0);
            drawBitmapMatrix(&canvas, right, rightMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                rightMatrix.postTranslate(0, 2 * height);
                drawBitmapMatrix(&canvas, right, rightMatrix);
            }
            patternBBox.fRight = deviceBounds.width();
        }
    }

    if (tileModes[1] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1);
        if (deviceBounds.top() < 0) {
            SkBitmap top;
            SkAssertResult(image->extractSubset(&top, subset));

            SkMatrix topMatrix;
            topMatrix.setScale(SK_Scalar1, -deviceBounds.top());
            topMatrix.postTranslate(0, deviceBounds.top());
            drawBitmapMatrix(&canvas, top, topMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                topMatrix.postScale(-1, 1);
                topMatrix.postTranslate(2 * width, 0);
                drawBitmapMatrix(&canvas, top, topMatrix);
            }
            patternBBox.fTop = 0;
        }

        if (deviceBounds.bottom() > height) {
            SkBitmap bottom;
            subset.offset(0, image->height() - 1);
            SkAssertResult(image->extractSubset(&bottom, subset));

            SkMatrix bottomMatrix;
            bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height);
            bottomMatrix.postTranslate(0, height);
            drawBitmapMatrix(&canvas, bottom, bottomMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                bottomMatrix.postScale(-1, 1);
                bottomMatrix.postTranslate(2 * width, 0);
                drawBitmapMatrix(&canvas, bottom, bottomMatrix);
            }
            patternBBox.fBottom = deviceBounds.height();
        }
    }

    // Put the canvas into the pattern stream (fContent).
    SkAutoTDelete<SkStreamAsset> content(patternDevice->content());

    SkPDFImageShader* imageShader =
            SkNEW_ARGS(SkPDFImageShader, (autoState->detach()));
    imageShader->setData(content.get());

    SkAutoTUnref<SkPDFDict> resourceDict(
            patternDevice->createResourceDict());
    populate_tiling_pattern_dict(imageShader, patternBBox,
                                 resourceDict.get(), finalMatrix);

    imageShader->fShaderState->fImage.unlockPixels();

    canon->addImageShader(imageShader);
    return imageShader;
}
Beispiel #29
0
static void test_matrix_homogeneous(skiatest::Reporter* reporter) {
    SkMatrix mat;

    const float kRotation0 = 15.5f;
    const float kRotation1 = -50.f;
    const float kScale0 = 5000.f;

    const int kTripleCount = 1000;
    const int kMatrixCount = 1000;
    SkRandom rand;

    SkScalar randTriples[3*kTripleCount];
    for (int i = 0; i < 3*kTripleCount; ++i) {
        randTriples[i] = rand.nextRangeF(-3000.f, 3000.f);
    }

    SkMatrix mats[kMatrixCount];
    for (int i = 0; i < kMatrixCount; ++i) {
        for (int j = 0; j < 9; ++j) {
            mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f));
        }
    }

    // identity
    {
    mat.reset();
    SkScalar dst[3*kTripleCount];
    mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
    REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3));
    }

    // zero matrix
    {
    mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f);
    SkScalar dst[3*kTripleCount];
    mat.mapHomogeneousPoints(dst, randTriples, kTripleCount);
    SkScalar zeros[3] = {0.f, 0.f, 0.f};
    for (int i = 0; i < kTripleCount; ++i) {
        REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3));
    }
    }

    // zero point
    {
    SkScalar zeros[3] = {0.f, 0.f, 0.f};
    for (int i = 0; i < kMatrixCount; ++i) {
        SkScalar dst[3];
        mats[i].mapHomogeneousPoints(dst, zeros, 1);
        REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3));
    }
    }

    // doesn't crash with null dst, src, count == 0
    {
    mats[0].mapHomogeneousPoints(NULL, NULL, 0);
    }

    // uniform scale of point
    {
    mat.setScale(kScale0, kScale0);
    SkScalar dst[3];
    SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
    SkPoint pnt;
    pnt.set(src[0], src[1]);
    mat.mapHomogeneousPoints(dst, src, 1);
    mat.mapPoints(&pnt, &pnt, 1);
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
    }

    // rotation of point
    {
    mat.setRotate(kRotation0);
    SkScalar dst[3];
    SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
    SkPoint pnt;
    pnt.set(src[0], src[1]);
    mat.mapHomogeneousPoints(dst, src, 1);
    mat.mapPoints(&pnt, &pnt, 1);
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
    }

    // rotation, scale, rotation of point
    {
    mat.setRotate(kRotation1);
    mat.postScale(kScale0, kScale0);
    mat.postRotate(kRotation0);
    SkScalar dst[3];
    SkScalar src[3] = {randTriples[0], randTriples[1], 1.f};
    SkPoint pnt;
    pnt.set(src[0], src[1]);
    mat.mapHomogeneousPoints(dst, src, 1);
    mat.mapPoints(&pnt, &pnt, 1);
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY));
    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1));
    }

    // compare with naive approach
    {
    for (int i = 0; i < kMatrixCount; ++i) {
        for (int j = 0; j < kTripleCount; ++j) {
            SkScalar dst[3];
            mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1);
            REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst));
        }
    }
    }

}
void draw_paths(SkCanvas* canvas, ShadowMode mode) {
    SkTArray<SkPath> paths;
    paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10);
    SkRRect oddRRect;
    oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16);
    paths.push_back().addRRect(oddRRect);
    paths.push_back().addRect(SkRect::MakeWH(50, 50));
    paths.push_back().addCircle(25, 25, 25);
    paths.push_back().cubicTo(100, 50, 20, 100, 0, 0);
    paths.push_back().addOval(SkRect::MakeWH(20, 60));

    // star
    SkTArray<SkPath> concavePaths;
    concavePaths.push_back().moveTo(0.0f, -33.3333f);
    concavePaths.back().lineTo(9.62f, -16.6667f);
    concavePaths.back().lineTo(28.867f, -16.6667f);
    concavePaths.back().lineTo(19.24f, 0.0f);
    concavePaths.back().lineTo(28.867f, 16.6667f);
    concavePaths.back().lineTo(9.62f, 16.6667f);
    concavePaths.back().lineTo(0.0f, 33.3333f);
    concavePaths.back().lineTo(-9.62f, 16.6667f);
    concavePaths.back().lineTo(-28.867f, 16.6667f);
    concavePaths.back().lineTo(-19.24f, 0.0f);
    concavePaths.back().lineTo(-28.867f, -16.6667f);
    concavePaths.back().lineTo(-9.62f, -16.6667f);
    concavePaths.back().close();

    // dumbbell
    concavePaths.push_back().moveTo(50, 0);
    concavePaths.back().cubicTo(100, 25, 60, 50, 50, 0);
    concavePaths.back().cubicTo(0, -25, 40, -50, 50, 0);

    static constexpr SkScalar kPad = 15.f;
    static constexpr SkScalar kLightR = 100.f;
    static constexpr SkScalar kHeight = 50.f;

    // transform light position relative to canvas to handle tiling
    SkPoint lightXY = canvas->getTotalMatrix().mapXY(250, 400);
    SkPoint3 lightPos = { lightXY.fX, lightXY.fY, 500 };

    canvas->translate(3 * kPad, 3 * kPad);
    canvas->save();
    SkScalar x = 0;
    SkScalar dy = 0;
    SkTDArray<SkMatrix> matrices;
    matrices.push()->reset();
    SkMatrix* m = matrices.push();
    m->setRotate(33.f, 25.f, 25.f);
    m->postScale(1.2f, 0.8f, 25.f, 25.f);
    for (auto& m : matrices) {
        for (int flags : { kNone_ShadowFlag, kTransparentOccluder_ShadowFlag }) {
            for (const auto& path : paths) {
                SkRect postMBounds = path.getBounds();
                m.mapRect(&postMBounds);
                SkScalar w = postMBounds.width() + kHeight;
                SkScalar dx = w + kPad;
                if (x + dx > kW - 3 * kPad) {
                    canvas->restore();
                    canvas->translate(0, dy);
                    canvas->save();
                    x = 0;
                    dy = 0;
                }

                canvas->save();
                canvas->concat(m);

                if (kDebugColorNoOccluders == mode || kDebugColorOccluders == mode) {
                    draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR,
                                true, flags);
                    draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR,
                                false, flags);
                } else if (kGrayscale == mode) {
                    SkColor ambientColor = SkColorSetARGB(0.1f * 255, 0, 0, 0);
                    SkColor spotColor = SkColorSetARGB(0.25f * 255, 0, 0, 0);
                    SkShadowUtils::DrawShadow(canvas, path, SkPoint3{0, 0, kHeight}, lightPos,
                                              kLightR, ambientColor, spotColor, flags);
                }

                SkPaint paint;
                paint.setAntiAlias(true);
                if (kDebugColorNoOccluders == mode) {
                    // Draw the path outline in green on top of the ambient and spot shadows.
                    if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) {
                        paint.setColor(SK_ColorCYAN);
                    } else {
                        paint.setColor(SK_ColorGREEN);
                    }
                    paint.setStyle(SkPaint::kStroke_Style);
                    paint.setStrokeWidth(0);
                } else {
                    paint.setColor(kDebugColorOccluders == mode ? SK_ColorLTGRAY : SK_ColorWHITE);
                    if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) {
                        paint.setAlpha(128);
                    }
                    paint.setStyle(SkPaint::kFill_Style);
                }
                canvas->drawPath(path, paint);
                canvas->restore();

                canvas->translate(dx, 0);
                x += dx;
                dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
            }
        }
    }

    // concave paths
    canvas->restore();
    canvas->translate(kPad, dy);
    canvas->save();
    x = kPad;
    dy = 0;
    for (auto& m : matrices) {
        // for the concave paths we are not clipping, so transparent and opaque are the same
        for (const auto& path : concavePaths) {
            SkRect postMBounds = path.getBounds();
            m.mapRect(&postMBounds);
            SkScalar w = postMBounds.width() + kHeight;
            SkScalar dx = w + kPad;

            canvas->save();
            canvas->concat(m);

            if (kDebugColorNoOccluders == mode || kDebugColorOccluders == mode) {
                draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR,
                            true, kNone_ShadowFlag);
                draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR,
                            false, kNone_ShadowFlag);
            } else if (kGrayscale == mode) {
                SkColor ambientColor = SkColorSetARGB(0.1f * 255, 0, 0, 0);
                SkColor spotColor = SkColorSetARGB(0.25f * 255, 0, 0, 0);
                SkShadowUtils::DrawShadow(canvas, path, SkPoint3{ 0, 0, kHeight }, lightPos,
                                          kLightR, ambientColor, spotColor, kNone_ShadowFlag);
            }

            SkPaint paint;
            paint.setAntiAlias(true);
            if (kDebugColorNoOccluders == mode) {
                // Draw the path outline in green on top of the ambient and spot shadows.
                paint.setColor(SK_ColorGREEN);
                paint.setStyle(SkPaint::kStroke_Style);
                paint.setStrokeWidth(0);
            } else {
                paint.setColor(kDebugColorOccluders == mode ? SK_ColorLTGRAY : SK_ColorWHITE);
                paint.setStyle(SkPaint::kFill_Style);
            }
            canvas->drawPath(path, paint);
            canvas->restore();

            canvas->translate(dx, 0);
            x += dx;
            dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
        }
    }

    // Show where the light is in x,y as a circle (specified in device space).
    SkMatrix invCanvasM = canvas->getTotalMatrix();
    if (invCanvasM.invert(&invCanvasM)) {
        canvas->save();
        canvas->concat(invCanvasM);
        SkPaint paint;
        paint.setColor(SK_ColorBLACK);
        paint.setAntiAlias(true);
        canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint);
        canvas->restore();
    }
}