当前位置: 首页>>代码示例>>C++>>正文


C++ SkMatrix类代码示例

本文整理汇总了C++中SkMatrix的典型用法代码示例。如果您正苦于以下问题:C++ SkMatrix类的具体用法?C++ SkMatrix怎么用?C++ SkMatrix使用的例子?那么, 这里精选的类代码示例或许可以为您提供帮助。


在下文中一共展示了SkMatrix类的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C++代码示例。

示例1: GaussianBlur

GrTexture* GaussianBlur(GrContext* context,
                        GrTexture* srcTexture,
                        bool canClobberSrc,
                        const SkRect& dstBounds,
                        const SkRect* srcBounds,
                        float sigmaX,
                        float sigmaY,
                        GrTextureProvider::SizeConstraint constraint) {
    SkASSERT(context);
    SkIRect clearRect;
    int scaleFactorX, radiusX;
    int scaleFactorY, radiusY;
    int maxTextureSize = context->caps()->maxTextureSize();
    sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
    sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);

    SkPoint srcOffset = SkPoint::Make(-dstBounds.x(), -dstBounds.y());
    SkRect localDstBounds = SkRect::MakeWH(dstBounds.width(), dstBounds.height());
    SkRect localSrcBounds;
    SkRect srcRect;
    if (srcBounds) {
        srcRect = localSrcBounds = *srcBounds;
        srcRect.offset(srcOffset);
        srcBounds = &localSrcBounds;
    } else {
        srcRect = localDstBounds;
    }

    scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
    srcRect.roundOut(&srcRect);
    scale_rect(&srcRect, static_cast<float>(scaleFactorX),
                         static_cast<float>(scaleFactorY));

    // setup new clip
    GrClip clip(localDstBounds);

    SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
             kRGBA_8888_GrPixelConfig == srcTexture->config() ||
             kAlpha_8_GrPixelConfig == srcTexture->config());

    GrSurfaceDesc desc;
    desc.fFlags = kRenderTarget_GrSurfaceFlag;
    desc.fWidth = SkScalarFloorToInt(dstBounds.width());
    desc.fHeight = SkScalarFloorToInt(dstBounds.height());
    desc.fConfig = srcTexture->config();

    GrTexture* dstTexture;
    GrTexture* tempTexture;
    SkAutoTUnref<GrTexture> temp1, temp2;

    temp1.reset(context->textureProvider()->createTexture(desc, constraint));
    dstTexture = temp1.get();
    if (canClobberSrc) {
        tempTexture = srcTexture;
    } else {
        temp2.reset(context->textureProvider()->createTexture(desc, constraint));
        tempTexture = temp2.get();
    }

    if (nullptr == dstTexture || nullptr == tempTexture) {
        return nullptr;
    }

    SkAutoTUnref<GrDrawContext> srcDrawContext;

    for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
        GrPaint paint;
        SkMatrix matrix;
        matrix.setIDiv(srcTexture->width(), srcTexture->height());
        SkRect dstRect(srcRect);
        if (srcBounds && i == 1) {
            SkRect domain;
            matrix.mapRect(&domain, *srcBounds);
            domain.inset((i < scaleFactorX) ? SK_ScalarHalf / srcTexture->width() : 0.0f,
                         (i < scaleFactorY) ? SK_ScalarHalf / srcTexture->height() : 0.0f);
            SkAutoTUnref<const GrFragmentProcessor> fp(GrTextureDomainEffect::Create(
                srcTexture,
                matrix,
                domain,
                GrTextureDomain::kDecal_Mode,
                GrTextureParams::kBilerp_FilterMode));
            paint.addColorFragmentProcessor(fp);
            srcRect.offset(-srcOffset);
            srcOffset.set(0, 0);
        } else {
            GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
            paint.addColorTextureProcessor(srcTexture, matrix, params);
        }
        paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
        scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
                             i < scaleFactorY ? 0.5f : 1.0f);

        SkAutoTUnref<GrDrawContext> dstDrawContext(
                                             context->drawContext(dstTexture->asRenderTarget()));
        if (!dstDrawContext) {
            return nullptr;
        }
        dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect);

        srcDrawContext.swap(dstDrawContext);
//.........这里部分代码省略.........
开发者ID:keinvo,项目名称:skia,代码行数:101,代码来源:SkGpuBlurUtils.cpp

示例2: computeSize

static SkSize computeSize(const SkBitmap& bm, const SkMatrix& mat) {
    SkRect bounds = SkRect::MakeWH(SkIntToScalar(bm.width()),
                                   SkIntToScalar(bm.height()));
    mat.mapRect(&bounds);
    return SkSize::Make(bounds.width(), bounds.height());
}
开发者ID:guolianzhu,项目名称:skia,代码行数:6,代码来源:filterindiabox.cpp

示例3: test_fade

// test drawing with strips of fading gradient above and below
static void test_fade(SkCanvas* canvas) {
    SkAutoCanvasRestore ar(canvas, true);

    SkRect r;

    SkPaint p;
    p.setAlpha(0x88);

    SkAutoCanvasRestore ar2(canvas, false);

    // create the layers

    r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
    canvas->clipRect(r);

    r.fBottom = SkIntToScalar(20);
    canvas->saveLayer(&r, NULL);

    r.fTop = SkIntToScalar(80);
    r.fBottom = SkIntToScalar(100);
    canvas->saveLayer(&r, NULL);

    // now draw the "content"

    if (true) {
        r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));

        canvas->saveLayerAlpha(&r, 0x80);

        SkPaint p;
        p.setColor(SK_ColorRED);
        p.setAntiAlias(true);
        canvas->drawOval(r, p);

        dump_layers("inside layer alpha", canvas);

        canvas->restore();
    } else {
        r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));

        SkPaint p;
        p.setColor(SK_ColorRED);
        p.setAntiAlias(true);
        canvas->drawOval(r, p);
    }

//    return;

    dump_layers("outside layer alpha", canvas);

    // now apply an effect
    SkMatrix m;
    m.setScale(SK_Scalar1, -SK_Scalar1);
    m.postTranslate(0, SkIntToScalar(100));

    SkPaint paint;
    make_paint(&paint, m);
    r.set(0, 0, SkIntToScalar(100), SkIntToScalar(20));
//    SkDebugf("--------- draw top grad\n");
    canvas->drawRect(r, paint);

    r.fTop = SkIntToScalar(80);
    r.fBottom = SkIntToScalar(100);
//    SkDebugf("--------- draw bot grad\n");
    canvas->drawRect(r, paint);
}
开发者ID:nikolayvoronchikhin,项目名称:skia,代码行数:67,代码来源:SampleLayers.cpp

示例4: SkASSERT

void SkScalerContext::getImage(const SkGlyph& origGlyph) {
    const SkGlyph*  glyph = &origGlyph;
    SkGlyph         tmpGlyph;

    if (fMaskFilter) {   // restore the prefilter bounds
        tmpGlyph.init(origGlyph.fID);

        // need the original bounds, sans our maskfilter
        SkMaskFilter* mf = fMaskFilter;
        fMaskFilter = NULL;             // temp disable
        this->getMetrics(&tmpGlyph);
        fMaskFilter = mf;               // restore

        tmpGlyph.fImage = origGlyph.fImage;

        // we need the prefilter bounds to be <= filter bounds
        SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
        SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
        glyph = &tmpGlyph;
    }

    if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) {
        SkPath      devPath, fillPath;
        SkMatrix    fillToDevMatrix;

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

        const bool lcdMode = fRec.fMaskFormat == SkMask::kHorizontalLCD_Format ||
                             fRec.fMaskFormat == SkMask::kVerticalLCD_Format;

        if (fRasterizer) {
            SkMask  mask;

            glyph->toMask(&mask);
            mask.fFormat = SkMask::kA8_Format;
            sk_bzero(glyph->fImage, mask.computeImageSize());

            if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
                                        fMaskFilter, &mask,
                                        SkMask::kJustRenderImage_CreateMode)) {
                return;
            }
        } else {
            SkBitmap    bm;
            SkBitmap::Config config;
            SkMatrix    matrix;
            SkRegion    clip;
            SkPaint     paint;
            SkDraw      draw;

            if (SkMask::kA8_Format == fRec.fMaskFormat || lcdMode) {
                config = SkBitmap::kA8_Config;
                paint.setAntiAlias(true);
            } else {
                SkASSERT(SkMask::kBW_Format == fRec.fMaskFormat);
                config = SkBitmap::kA1_Config;
                paint.setAntiAlias(false);
            }

            clip.setRect(0, 0, glyph->fWidth, glyph->fHeight);
            matrix.setTranslate(-SkIntToScalar(glyph->fLeft),
                                -SkIntToScalar(glyph->fTop));
            bm.setConfig(config, glyph->fWidth, glyph->fHeight,
                         glyph->rowBytes());
            bm.setPixels(glyph->fImage);
            sk_bzero(glyph->fImage, bm.height() * bm.rowBytes());

            draw.fClip  = &clip;
            draw.fMatrix = &matrix;
            draw.fBitmap = &bm;
            draw.fBounder = NULL;
            draw.drawPath(devPath, paint);
        }

        if (lcdMode)
            glyph->expandA8ToLCD();
    } else {
        this->getGlyphContext(*glyph)->generateImage(*glyph);
    }

    if (fMaskFilter) {
        SkMask      srcM, dstM;
        SkMatrix    matrix;

        // the src glyph image shouldn't be 3D
        SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
        glyph->toMask(&srcM);
        fRec.getMatrixFrom2x2(&matrix);

        if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {
            int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width());
            int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height());
            int dstRB = origGlyph.rowBytes();
            int srcRB = dstM.fRowBytes;

            const uint8_t* src = (const uint8_t*)dstM.fImage;
            uint8_t* dst = (uint8_t*)origGlyph.fImage;

            if (SkMask::k3D_Format == dstM.fFormat) {
                // we have to copy 3 times as much
//.........这里部分代码省略.........
开发者ID:achellies,项目名称:DUI_LIb,代码行数:101,代码来源:SkScalerContext.cpp

示例5: cull_line

// Attempt to trim the line to minimally cover the cull rect (currently
// only works for horizontal and vertical lines).
// Return true if processing should continue; false otherwise.
static bool cull_line(SkPoint* pts, const SkStrokeRec& rec,
                      const SkMatrix& ctm, const SkRect* cullRect,
                      const SkScalar intervalLength) {
    if (nullptr == cullRect) {
        SkASSERT(false); // Shouldn't ever occur in practice
        return false;
    }

    SkScalar dx = pts[1].x() - pts[0].x();
    SkScalar dy = pts[1].y() - pts[0].y();

    if ((dx && dy) || (!dx && !dy)) {
        return false;
    }

    SkRect bounds = *cullRect;
    outset_for_stroke(&bounds, rec);

    // cullRect is in device space while pts are in the local coordinate system
    // defined by the ctm. We want our answer in the local coordinate system.

    SkASSERT(ctm.rectStaysRect());
    SkMatrix inv;
    if (!ctm.invert(&inv)) {
        return false;
    }

    inv.mapRect(&bounds);

    if (dx) {
        SkASSERT(dx && !dy);
        SkScalar minX = pts[0].fX;
        SkScalar maxX = pts[1].fX;

        if (dx < 0) {
            SkTSwap(minX, maxX);
        }

        SkASSERT(minX < maxX);
        if (maxX <= bounds.fLeft || minX >= bounds.fRight) {
            return false;
        }

        // Now we actually perform the chop, removing the excess to the left and
        // right of the bounds (keeping our new line "in phase" with the dash,
        // hence the (mod intervalLength).

        if (minX < bounds.fLeft) {
            minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX, intervalLength);
        }
        if (maxX > bounds.fRight) {
            maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight, intervalLength);
        }

        SkASSERT(maxX > minX);
        if (dx < 0) {
            SkTSwap(minX, maxX);
        }
        pts[0].fX = minX;
        pts[1].fX = maxX;
    } else {
        SkASSERT(dy && !dx);
        SkScalar minY = pts[0].fY;
        SkScalar maxY = pts[1].fY;

        if (dy < 0) {
            SkTSwap(minY, maxY);
        }

        SkASSERT(minY < maxY);
        if (maxY <= bounds.fTop || minY >= bounds.fBottom) {
            return false;
        }

        // Now we actually perform the chop, removing the excess to the top and
        // bottom of the bounds (keeping our new line "in phase" with the dash,
        // hence the (mod intervalLength).

        if (minY < bounds.fTop) {
            minY = bounds.fTop - SkScalarMod(bounds.fTop - minY, intervalLength);
        }
        if (maxY > bounds.fBottom) {
            maxY = bounds.fBottom + SkScalarMod(maxY - bounds.fBottom, intervalLength);
        }

        SkASSERT(maxY > minY);
        if (dy < 0) {
            SkTSwap(minY, maxY);
        }
        pts[0].fY = minY;
        pts[1].fY = maxY;
    }

    return true;
}
开发者ID:molikto,项目名称:Skia,代码行数:98,代码来源:SkDashPathEffect.cpp

示例6: computeShapeMatrix

bool SkScalerContext_CairoFT::computeShapeMatrix(const SkMatrix& m)
{
    // Compute a shape matrix compatible with Cairo's _compute_transform.
    // Finds major/minor scales and uses them to normalize the transform.
    double scaleX = m.getScaleX();
    double skewX = m.getSkewX();
    double skewY = m.getSkewY();
    double scaleY = m.getScaleY();
    double det = scaleX * scaleY - skewY * skewX;
    if (!std::isfinite(det)) {
        fScaleX = fRec.fTextSize * fRec.fPreScaleX;
        fScaleY = fRec.fTextSize;
        fHaveShape = false;
        return false;
    }
    double major = det != 0.0 ? hypot(scaleX, skewY) : 0.0;
    double minor = major != 0.0 ? fabs(det) / major : 0.0;
    // Limit scales to be above 1pt.
    major = SkTMax(major, 1.0);
    minor = SkTMax(minor, 1.0);

    // If the font is not scalable, then choose the best available size.
    CairoLockedFTFace faceLock(fScaledFont);
    FT_Face face = faceLock.getFace();
    if (face && !FT_IS_SCALABLE(face)) {
        double bestDist = DBL_MAX;
        FT_Int bestSize = -1;
        for (FT_Int i = 0; i < face->num_fixed_sizes; i++) {
            // Distance is positive if strike is larger than desired size,
            // or negative if smaller. If previously a found smaller strike,
            // then prefer a larger strike. Otherwise, minimize distance.
            double dist = face->available_sizes[i].y_ppem / 64.0 - minor;
            if (bestDist < 0 ? dist >= bestDist : fabs(dist) <= bestDist) {
                bestDist = dist;
                bestSize = i;
            }
        }
        if (bestSize < 0) {
            fScaleX = fRec.fTextSize * fRec.fPreScaleX;
            fScaleY = fRec.fTextSize;
            fHaveShape = false;
            return false;
        }
        major = face->available_sizes[bestSize].x_ppem / 64.0;
        minor = face->available_sizes[bestSize].y_ppem / 64.0;
        fHaveShape = true;
    } else {
        fHaveShape = !m.isScaleTranslate();
    }

    fScaleX = SkDoubleToScalar(major);
    fScaleY = SkDoubleToScalar(minor);

    if (fHaveShape) {
        // Normalize the transform and convert to fixed-point.
        double invScaleX = 65536.0 / major;
        double invScaleY = 65536.0 / minor;
        fShapeMatrix.xx = (FT_Fixed)(scaleX * invScaleX);
        fShapeMatrix.yx = -(FT_Fixed)(skewY * invScaleX);
        fShapeMatrix.xy = -(FT_Fixed)(skewX * invScaleY);
        fShapeMatrix.yy = (FT_Fixed)(scaleY * invScaleY);
    }
    return true;
}
开发者ID:zbraniecki,项目名称:gecko-dev,代码行数:64,代码来源:SkFontHost_cairo.cpp

示例7: SkPDFDict

SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
        : SkPDFDict("Pattern"),
          fState(state) {
    SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL;
    SkPoint transformPoints[2];

    // Depending on the type of the gradient, we want to transform the
    // coordinate space in different ways.
    const SkShader::GradientInfo* info = &fState.get()->fInfo;
    transformPoints[0] = info->fPoint[0];
    transformPoints[1] = info->fPoint[1];
    switch (fState.get()->fType) {
        case SkShader::kLinear_GradientType:
            codeFunction = &linearCode;
            break;
        case SkShader::kRadial_GradientType:
            transformPoints[1] = transformPoints[0];
            transformPoints[1].fX += info->fRadius[0];
            codeFunction = &radialCode;
            break;
        case SkShader::kRadial2_GradientType: {
            // Bail out if the radii are the same.  Empty fResources signals
            // an error and isValid will return false.
            if (info->fRadius[0] == info->fRadius[1]) {
                return;
            }
            transformPoints[1] = transformPoints[0];
            SkScalar dr = info->fRadius[1] - info->fRadius[0];
            transformPoints[1].fX += dr;
            codeFunction = &twoPointRadialCode;
            break;
        }
        case SkShader::kSweep_GradientType:
            transformPoints[1] = transformPoints[0];
            transformPoints[1].fX += SK_Scalar1;
            codeFunction = &sweepCode;
            break;
        case SkShader::kColor_GradientType:
        case SkShader::kNone_GradientType:
        default:
            return;
    }

    // Move any scaling (assuming a unit gradient) or translation
    // (and rotation for linear gradient), of the final gradient from
    // info->fPoints to the matrix (updating bbox appropriately).  Now
    // the gradient can be drawn on on the unit segment.
    SkMatrix mapperMatrix;
    unitToPointsMatrix(transformPoints, &mapperMatrix);
    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
    finalMatrix.preConcat(mapperMatrix);
    finalMatrix.preConcat(fState.get()->fShaderTransform);
    SkRect bbox;
    bbox.set(fState.get()->fBBox);
    if (!transformBBox(finalMatrix, &bbox)) {
        return;
    }

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

    SkString functionCode;
    // The two point radial gradient further references fState.get()->fInfo
    // in translating from x, y coordinates to the t parameter. So, we have
    // to transform the points and radii according to the calculated matrix.
    if (fState.get()->fType == SkShader::kRadial2_GradientType) {
        SkShader::GradientInfo twoPointRadialInfo = *info;
        SkMatrix inverseMapperMatrix;
        if (!mapperMatrix.invert(&inverseMapperMatrix)) {
            return;
        }
        inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
        twoPointRadialInfo.fRadius[0] =
            inverseMapperMatrix.mapRadius(info->fRadius[0]);
        twoPointRadialInfo.fRadius[1] =
            inverseMapperMatrix.mapRadius(info->fRadius[1]);
        functionCode = codeFunction(twoPointRadialInfo);
    } else {
        functionCode = codeFunction(*info);
    }

    SkRefPtr<SkPDFStream> function = makePSFunction(functionCode, domain.get());
    // Pass one reference to fResources, SkRefPtr and new both took a reference.
    fResources.push(function.get());

    SkRefPtr<SkPDFDict> pdfShader = new SkPDFDict;
    pdfShader->unref();  // SkRefPtr and new both took a reference.
    pdfShader->insertInt("ShadingType", 1);
    pdfShader->insertName("ColorSpace", "DeviceRGB");
    pdfShader->insert("Domain", domain.get());
    pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref();

    insertInt("PatternType", 2);
    insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
    insert("Shading", pdfShader.get());
//.........这里部分代码省略.........
开发者ID:Beifeng,项目名称:WTL-DUI,代码行数:101,代码来源:SkPDFShader.cpp

示例8: GetLocalBounds

void GetLocalBounds(const SkPath& path, const SkDrawShadowRec& rec, const SkMatrix& ctm,
                    SkRect* bounds) {
    SkRect ambientBounds = path.getBounds();
    SkScalar occluderZ;
    if (SkScalarNearlyZero(rec.fZPlaneParams.fX) && SkScalarNearlyZero(rec.fZPlaneParams.fY)) {
        occluderZ = rec.fZPlaneParams.fZ;
    } else {
        occluderZ = compute_z(ambientBounds.fLeft, ambientBounds.fTop, rec.fZPlaneParams);
        occluderZ = SkTMax(occluderZ, compute_z(ambientBounds.fRight, ambientBounds.fTop,
                                                rec.fZPlaneParams));
        occluderZ = SkTMax(occluderZ, compute_z(ambientBounds.fLeft, ambientBounds.fBottom,
                                                rec.fZPlaneParams));
        occluderZ = SkTMax(occluderZ, compute_z(ambientBounds.fRight, ambientBounds.fBottom,
                                                rec.fZPlaneParams));
    }
    SkScalar ambientBlur;
    SkScalar spotBlur;
    SkScalar spotScale;
    SkPoint spotOffset;
    if (ctm.hasPerspective()) {
        // transform ambient and spot bounds into device space
        ctm.mapRect(&ambientBounds);

        // get ambient blur (in device space)
        ambientBlur = SkDrawShadowMetrics::AmbientBlurRadius(occluderZ);

        // get spot params (in device space)
        SkPoint devLightPos = SkPoint::Make(rec.fLightPos.fX, rec.fLightPos.fY);
        ctm.mapPoints(&devLightPos, 1);
        SkDrawShadowMetrics::GetSpotParams(occluderZ, devLightPos.fX, devLightPos.fY,
                                           rec.fLightPos.fZ, rec.fLightRadius,
                                           &spotBlur, &spotScale, &spotOffset);
    } else {
        SkScalar devToSrcScale = SkScalarInvert(ctm.getMinScale());

        // get ambient blur (in local space)
        SkScalar devSpaceAmbientBlur = SkDrawShadowMetrics::AmbientBlurRadius(occluderZ);
        ambientBlur = devSpaceAmbientBlur*devToSrcScale;

        // get spot params (in local space)
        SkDrawShadowMetrics::GetSpotParams(occluderZ, rec.fLightPos.fX, rec.fLightPos.fY,
                                           rec.fLightPos.fZ, rec.fLightRadius,
                                           &spotBlur, &spotScale, &spotOffset);

        // convert spot blur to local space
        spotBlur *= devToSrcScale;
    }

    // in both cases, adjust ambient and spot bounds
    SkRect spotBounds = ambientBounds;
    ambientBounds.outset(ambientBlur, ambientBlur);
    spotBounds.fLeft *= spotScale;
    spotBounds.fTop *= spotScale;
    spotBounds.fRight *= spotScale;
    spotBounds.fBottom *= spotScale;
    spotBounds.offset(spotOffset.fX, spotOffset.fY);
    spotBounds.outset(spotBlur, spotBlur);

    // merge bounds
    *bounds = ambientBounds;
    bounds->join(spotBounds);
    // outset a bit to account for floating point error
    bounds->outset(1, 1);

    // if perspective, transform back to src space
    if (ctm.hasPerspective()) {
        // TODO: create tighter mapping from dev rect back to src rect
        SkMatrix inverse;
        if (ctm.invert(&inverse)) {
            inverse.mapRect(bounds);
        }
    }
}
开发者ID:jasonLaster,项目名称:gecko-dev,代码行数:73,代码来源:SkDrawShadowInfo.cpp

示例9: GetSpotShadowTransform

bool GetSpotShadowTransform(const SkPoint3& lightPos, SkScalar lightRadius,
                            const SkMatrix& ctm, const SkPoint3& zPlaneParams,
                            const SkRect& pathBounds, SkMatrix* shadowTransform, SkScalar* radius) {
    auto heightFunc = [zPlaneParams] (SkScalar x, SkScalar y) {
        return zPlaneParams.fX*x + zPlaneParams.fY*y + zPlaneParams.fZ;
    };
    SkScalar occluderHeight = heightFunc(pathBounds.centerX(), pathBounds.centerY());

    if (!ctm.hasPerspective()) {
        SkScalar scale;
        SkVector translate;
        SkDrawShadowMetrics::GetSpotParams(occluderHeight, lightPos.fX, lightPos.fY, lightPos.fZ,
                                           lightRadius, radius, &scale, &translate);
        shadowTransform->setScaleTranslate(scale, scale, translate.fX, translate.fY);
        shadowTransform->preConcat(ctm);
    } else {
        if (SkScalarNearlyZero(pathBounds.width()) || SkScalarNearlyZero(pathBounds.height())) {
            return false;
        }

        // get rotated quad in 3D
        SkPoint pts[4];
        ctm.mapRectToQuad(pts, pathBounds);
        // No shadows for bowties or other degenerate cases
        if (!SkIsConvexPolygon(pts, 4)) {
            return false;
        }
        SkPoint3 pts3D[4];
        SkScalar z = heightFunc(pathBounds.fLeft, pathBounds.fTop);
        pts3D[0].set(pts[0].fX, pts[0].fY, z);
        z = heightFunc(pathBounds.fRight, pathBounds.fTop);
        pts3D[1].set(pts[1].fX, pts[1].fY, z);
        z = heightFunc(pathBounds.fRight, pathBounds.fBottom);
        pts3D[2].set(pts[2].fX, pts[2].fY, z);
        z = heightFunc(pathBounds.fLeft, pathBounds.fBottom);
        pts3D[3].set(pts[3].fX, pts[3].fY, z);

        // project from light through corners to z=0 plane
        for (int i = 0; i < 4; ++i) {
            SkScalar dz = lightPos.fZ - pts3D[i].fZ;
            // light shouldn't be below or at a corner's z-location
            if (dz <= SK_ScalarNearlyZero) {
                return false;
            }
            SkScalar zRatio = pts3D[i].fZ / dz;
            pts3D[i].fX -= (lightPos.fX - pts3D[i].fX)*zRatio;
            pts3D[i].fY -= (lightPos.fY - pts3D[i].fY)*zRatio;
            pts3D[i].fZ = SK_Scalar1;
        }

        // Generate matrix that projects from [-1,1]x[-1,1] square to projected quad
        SkPoint3 h0, h1, h2;
        // Compute homogenous crossing point between top and bottom edges (gives new x-axis).
        h0 = (pts3D[1].cross(pts3D[0])).cross(pts3D[2].cross(pts3D[3]));
        // Compute homogenous crossing point between left and right edges (gives new y-axis).
        h1 = (pts3D[0].cross(pts3D[3])).cross(pts3D[1].cross(pts3D[2]));
        // Compute homogenous crossing point between diagonals (gives new origin).
        h2 = (pts3D[0].cross(pts3D[2])).cross(pts3D[1].cross(pts3D[3]));
        // If h2 is a vector (z=0 in 2D homogeneous space), that means that at least
        // two of the quad corners are coincident and we don't have a realistic projection
        if (SkScalarNearlyZero(h2.fZ)) {
            return false;
        }
        // In some cases the crossing points are in the wrong direction
        // to map (-1,-1) to pts3D[0], so we need to correct for that.
        // Want h0 to be to the right of the left edge.
        SkVector3 v = pts3D[3] - pts3D[0];
        SkVector3 w = h0 - pts3D[0];
        SkScalar perpDot = v.fX*w.fY - v.fY*w.fX;
        if (perpDot > 0) {
            h0 = -h0;
        }
        // Want h1 to be above the bottom edge.
        v = pts3D[1] - pts3D[0];
        perpDot = v.fX*w.fY - v.fY*w.fX;
        if (perpDot < 0) {
            h1 = -h1;
        }
        shadowTransform->setAll(h0.fX / h2.fZ, h1.fX / h2.fZ, h2.fX / h2.fZ,
                               h0.fY / h2.fZ, h1.fY / h2.fZ, h2.fY / h2.fZ,
                               h0.fZ / h2.fZ, h1.fZ / h2.fZ, 1);
        // generate matrix that transforms from bounds to [-1,1]x[-1,1] square
        SkMatrix toHomogeneous;
        SkScalar xScale = 2/(pathBounds.fRight - pathBounds.fLeft);
        SkScalar yScale = 2/(pathBounds.fBottom - pathBounds.fTop);
        toHomogeneous.setAll(xScale, 0, -xScale*pathBounds.fLeft - 1,
                             0, yScale, -yScale*pathBounds.fTop - 1,
                             0, 0, 1);
        shadowTransform->preConcat(toHomogeneous);

        *radius = SkDrawShadowMetrics::SpotBlurRadius(occluderHeight, lightPos.fZ, lightRadius);
    }

    return true;
}
开发者ID:jasonLaster,项目名称:gecko-dev,代码行数:95,代码来源:SkDrawShadowInfo.cpp

示例10: TestMatrix

void TestMatrix(skiatest::Reporter* reporter) {
    SkMatrix    mat, inverse, iden1, iden2;

    mat.reset();
    mat.setTranslate(SK_Scalar1, SK_Scalar1);
    mat.invert(&inverse);
    iden1.setConcat(mat, inverse);
    REPORTER_ASSERT(reporter, is_identity(iden1));

    mat.setScale(SkIntToScalar(2), SkIntToScalar(2));
    mat.invert(&inverse);
    iden1.setConcat(mat, inverse);
    REPORTER_ASSERT(reporter, is_identity(iden1));
    test_flatten(reporter, mat);

    mat.setScale(SK_Scalar1/2, SK_Scalar1/2);
    mat.invert(&inverse);
    iden1.setConcat(mat, inverse);
    REPORTER_ASSERT(reporter, is_identity(iden1));
    test_flatten(reporter, mat);

    mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
    mat.postRotate(SkIntToScalar(25));
    REPORTER_ASSERT(reporter, mat.invert(NULL));
    mat.invert(&inverse);
    iden1.setConcat(mat, inverse);
    REPORTER_ASSERT(reporter, is_identity(iden1));
    iden2.setConcat(inverse, mat);
    REPORTER_ASSERT(reporter, is_identity(iden2));
    test_flatten(reporter, mat);
    test_flatten(reporter, iden2);

    // rectStaysRect test
    {
        static const struct {
            SkScalar    m00, m01, m10, m11;
            bool        mStaysRect;
        }
        gRectStaysRectSamples[] = {
            {          0,          0,          0,           0, false },
            {          0,          0,          0,  SK_Scalar1, false },
            {          0,          0, SK_Scalar1,           0, false },
            {          0,          0, SK_Scalar1,  SK_Scalar1, false },
            {          0, SK_Scalar1,          0,           0, false },
            {          0, SK_Scalar1,          0,  SK_Scalar1, false },
            {          0, SK_Scalar1, SK_Scalar1,           0, true },
            {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
            { SK_Scalar1,          0,          0,           0, false },
            { SK_Scalar1,          0,          0,  SK_Scalar1, true },
            { SK_Scalar1,          0, SK_Scalar1,           0, false },
            { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
            { SK_Scalar1, SK_Scalar1,          0,           0, false },
            { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
            { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
            { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
        };

        for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
            SkMatrix    m;

            m.reset();
            m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
            m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
            m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
            m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
            REPORTER_ASSERT(reporter,
                    m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
        }
    }

    mat.reset();
    mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
    mat.set(SkMatrix::kMSkewX,  SkIntToScalar(2));
    mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
    mat.set(SkMatrix::kMSkewY,  SkIntToScalar(4));
    mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
    mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
    SkScalar affine[6];
    REPORTER_ASSERT(reporter, mat.asAffine(affine));

    #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
    REPORTER_ASSERT(reporter, affineEqual(ScaleX));
    REPORTER_ASSERT(reporter, affineEqual(SkewY));
    REPORTER_ASSERT(reporter, affineEqual(SkewX));
    REPORTER_ASSERT(reporter, affineEqual(ScaleY));
    REPORTER_ASSERT(reporter, affineEqual(TransX));
    REPORTER_ASSERT(reporter, affineEqual(TransY));
    #undef affineEqual

    mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
    REPORTER_ASSERT(reporter, !mat.asAffine(affine));

    SkMatrix mat2;
    mat2.reset();
    mat.reset();
    SkScalar zero = 0;
    mat.set(SkMatrix::kMSkewX, -zero);
    REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));

    mat2.reset();
//.........这里部分代码省略.........
开发者ID:ghub,项目名称:NVprSDK,代码行数:101,代码来源:MatrixTest.cpp

示例11: is_identity

static bool is_identity(const SkMatrix& m) {
    SkMatrix identity;
    identity.reset();
    return nearly_equal(m, identity);
}
开发者ID:ghub,项目名称:NVprSDK,代码行数:5,代码来源:MatrixTest.cpp

示例12: test_matrix_max_stretch

void test_matrix_max_stretch(skiatest::Reporter* reporter) {
    SkMatrix identity;
    identity.reset();
    REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());

    SkMatrix scale;
    scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
    REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch());

    SkMatrix rot90Scale;
    rot90Scale.setRotate(90 * SK_Scalar1);
    rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
    REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch());

    SkMatrix rotate;
    rotate.setRotate(128 * SK_Scalar1);
    REPORTER_ASSERT(reporter, SkScalarAbs(SK_Scalar1 - rotate.getMaxStretch()) <= SK_ScalarNearlyZero);

    SkMatrix translate;
    translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
    REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());

    SkMatrix perspX;
    perspX.reset();
    perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());

    SkMatrix perspY;
    perspY.reset();
    perspY.setPerspX(SkScalarToPersp(-SK_Scalar1 / 500));
    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());

    SkMatrix baseMats[] = {scale, rot90Scale, rotate,
                           translate, perspX, perspY};
    SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
    for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
        mats[i] = baseMats[i];
        bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
        REPORTER_ASSERT(reporter, invertable);
    }
    SkRandom rand;
    for (int m = 0; m < 1000; ++m) {
        SkMatrix mat;
        mat.reset();
        for (int i = 0; i < 4; ++i) {
            int x = rand.nextU() % SK_ARRAY_COUNT(mats);
            mat.postConcat(mats[x]);
        }
        SkScalar stretch = mat.getMaxStretch();
        
        if ((stretch < 0) != mat.hasPerspective()) {
            stretch = mat.getMaxStretch();
        }

        REPORTER_ASSERT(reporter, (stretch < 0) == mat.hasPerspective());

        if (mat.hasPerspective()) {
            m -= 1; // try another non-persp matrix
            continue;
        }

        // test a bunch of vectors. None should be scaled by more than stretch
        // (modulo some error) and we should find a vector that is scaled by
        // almost stretch.
        static const SkScalar gStretchTol = (105 * SK_Scalar1) / 100;
        static const SkScalar gMaxStretchTol = (97 * SK_Scalar1) / 100;
        SkScalar max = 0;
        SkVector vectors[1000];
        for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
            vectors[i].fX = rand.nextSScalar1();
            vectors[i].fY = rand.nextSScalar1();
            if (!vectors[i].normalize()) {
                i -= 1;
                continue;
            }
        }
        mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
        for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
            SkScalar d = vectors[i].length();
            REPORTER_ASSERT(reporter, SkScalarDiv(d, stretch) < gStretchTol);
            if (max < d) {
                max = d;
            }
        }
        REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol);
    }
}
开发者ID:ghub,项目名称:NVprSDK,代码行数:87,代码来源:MatrixTest.cpp

示例13: generateGlyphImage

void SkScalerContext_FreeType_Base::generateGlyphImage(
    FT_Face face,
    const SkGlyph& glyph,
    const SkMatrix& bitmapTransform)
{
    const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
    const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);

    switch ( face->glyph->format ) {
        case FT_GLYPH_FORMAT_OUTLINE: {
            FT_Outline* outline = &face->glyph->outline;
            FT_BBox     bbox;
            FT_Bitmap   target;

            int dx = 0, dy = 0;
            if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
                dx = SkFixedToFDot6(glyph.getSubXFixed());
                dy = SkFixedToFDot6(glyph.getSubYFixed());
                // negate dy since freetype-y-goes-up and skia-y-goes-down
                dy = -dy;
            }
            FT_Outline_Get_CBox(outline, &bbox);
            /*
                what we really want to do for subpixel is
                    offset(dx, dy)
                    compute_bounds
                    offset(bbox & !63)
                but that is two calls to offset, so we do the following, which
                achieves the same thing with only one offset call.
            */
            FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
                                          dy - ((bbox.yMin + dy) & ~63));

            if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
                FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD);
                SkMask mask;
                glyph.toMask(&mask);
                if (fPreBlend.isApplicable()) {
                    copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR,
                                       fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
                } else {
                    copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR,
                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
                }
            } else {
                target.width = glyph.fWidth;
                target.rows = glyph.fHeight;
                target.pitch = glyph.rowBytes();
                target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
                target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat);
                target.num_grays = 256;

                memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
                FT_Outline_Get_Bitmap(face->glyph->library, outline, &target);
            }
        } break;

        case FT_GLYPH_FORMAT_BITMAP: {
            FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode);
            SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);

            // Assume that the other formats do not exist.
            SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode ||
                     FT_PIXEL_MODE_GRAY == pixel_mode ||
                     FT_PIXEL_MODE_BGRA == pixel_mode);

            // These are the only formats this ScalerContext should request.
            SkASSERT(SkMask::kBW_Format == maskFormat ||
                     SkMask::kA8_Format == maskFormat ||
                     SkMask::kARGB32_Format == maskFormat ||
                     SkMask::kLCD16_Format == maskFormat);

            // If no scaling needed, directly copy glyph bitmap.
            if (bitmapTransform.isIdentity()) {
                SkMask dstMask;
                glyph.toMask(&dstMask);
                copyFTBitmap(face->glyph->bitmap, dstMask);
                break;
            }

            // Otherwise, scale the bitmap.

            // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB)
            SkBitmap unscaledBitmap;
            // TODO: mark this as sRGB when the blits will be sRGB.
            unscaledBitmap.allocPixels(SkImageInfo::Make(face->glyph->bitmap.width,
                                                         face->glyph->bitmap.rows,
                                                         SkColorType_for_FTPixelMode(pixel_mode),
                                                         kPremul_SkAlphaType));

            SkMask unscaledBitmapAlias;
            unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels());
            unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height());
            unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes();
            unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkColorType(unscaledBitmap.colorType());
            copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias);

            // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD.
            // BW requires an A8 target for resizing, which can then be down sampled.
            // LCD should use a 4x A8 target, which will then be down sampled.
//.........这里部分代码省略.........
开发者ID:03050903,项目名称:skia,代码行数:101,代码来源:SkFontHost_FreeType_common.cpp

示例14: SkASSERT

void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
                      const GlyphBuffer& glyphBuffer,  int from, int numGlyphs,
                      const FloatPoint& point) const
{
    // compile-time assert
    SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t));

    if (numGlyphs == 1 && glyphBuffer.glyphAt(from) == 0x3) {
        // Webkit likes to draw end text control command for some reason
        // Just ignore it
        return;
    }

    SkPaint paint;
    if (!setupForText(&paint, gc, font)) {
        return;
    }

    SkScalar                    x = SkFloatToScalar(point.x());
    SkScalar                    y = SkFloatToScalar(point.y());
    const GlyphBufferGlyph*     glyphs = glyphBuffer.glyphs(from);
    const GlyphBufferAdvance*   adv = glyphBuffer.advances(from);
    SkAutoSTMalloc<32, SkPoint> storage(numGlyphs), storage2(numGlyphs), storage3(numGlyphs);
    SkPoint*                    pos = storage.get();

    SkCanvas* canvas = gc->platformContext()->recordingCanvas();

    /*  We need an array of [x,y,x,y,x,y,...], but webkit is giving us
        point.xy + [width, height, width, height, ...], so we have to convert
     */

    if (font->platformData().orientation() == Vertical) {
        float yOffset = SkFloatToScalar(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent());
        gc->platformContext()->setTextOffset(FloatSize(0.0f, -yOffset)); // compensate for offset in bounds calculation
        y += yOffset;
    }

    if (EmojiFont::IsAvailable()) {
        // set filtering, to make scaled images look nice(r)
        paint.setFilterBitmap(true);

        SkMatrix rotator;
        rotator.reset();
        if (font->platformData().orientation() == Vertical) {
            canvas->save();
            canvas->rotate(-90);
            rotator.setRotate(90);
        }

        int localIndex = 0;
        int localCount = 0;
        for (int i = 0; i < numGlyphs; i++) {
            if (EmojiFont::IsEmojiGlyph(glyphs[i])) {
                if (localCount) {
                    rotator.mapPoints(&pos[localIndex], localCount);
                    canvas->drawPosText(&glyphs[localIndex],
                                     localCount * sizeof(uint16_t),
                                     &pos[localIndex], paint);
                }
                EmojiFont::Draw(canvas, glyphs[i], x, y, paint);
                // reset local index/count track for "real" glyphs
                localCount = 0;
                localIndex = i + 1;
            } else {
                pos[i].set(x, y);
                localCount += 1;
            }
            x += SkFloatToScalar(adv[i].width());
            y += SkFloatToScalar(adv[i].height());
        }

        // draw the last run of glyphs (if any)
        if (localCount) {
            rotator.mapPoints(&pos[localIndex], localCount);
            canvas->drawPosText(&glyphs[localIndex],
                             localCount * sizeof(uint16_t),
                             &pos[localIndex], paint);

        }

        if (font->platformData().orientation() == Vertical)
            canvas->restore();
    } else {
        for (int i = 0; i < numGlyphs; i++) {
            pos[i].set(x, y);
            y += SkFloatToScalar(adv[i].height());
            x += SkFloatToScalar(adv[i].width());
        }

        if (font->platformData().orientation() == Vertical) {
            canvas->save();
            canvas->rotate(-90);
            SkMatrix rotator;
            rotator.reset();
            rotator.setRotate(90);
            rotator.mapPoints(pos, numGlyphs);
        }
        canvas->drawPosText(glyphs,
            numGlyphs * sizeof(uint16_t), pos, paint);

//.........这里部分代码省略.........
开发者ID:KaoTD,项目名称:Nokia-RM-1013-2.0.0.11,代码行数:101,代码来源:FontAndroid.cpp

示例15: translate

static SkMatrix translate(SkScalar dx, SkScalar dy) {
    SkMatrix matrix;
    matrix.setTranslate(dx, dy);
    return matrix;
}
开发者ID:jagannathanraman,项目名称:skia,代码行数:5,代码来源:SkSweepGradient.cpp


注:本文中的SkMatrix类示例由纯净天空整理自Github/MSDocs等开源代码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。