Esempio n. 1
0
void GraphicsContext::concatCTM(const SkMatrix& affine)
{
	if (paintingDisabled())
		return;
	if (!affine.isIdentity())
		nvgTransform(platformContext()->canvas(), affine.get(SkMatrix::kMScaleX),
		affine.get(SkMatrix::kMSkewY), affine.get(SkMatrix::kMSkewX),
		affine.get(SkMatrix::kMScaleY), affine.get(SkMatrix::kMTransX),
		affine.get(SkMatrix::kMTransY));
		//platformContext()->canvas()->concat(affine);
}
static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
    if (!path.cheapComputeDirection(dir)) {
        return false;
    }
    // check whether m reverses the orientation
    SkASSERT(!m.hasPerspective());
    SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
                      SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
    if (det2x2 < 0) {
        *dir = SkPath::OppositeDirection(*dir);
    }
    return true;
}
Esempio n. 3
0
static void setup_MC_state(SkMCState* state, const SkMatrix& matrix, const SkRegion& clip) {
    // initialize the struct
    state->clipRectCount = 0;

    // capture the matrix
    for (int i = 0; i < 9; i++) {
        state->matrix[i] = matrix.get(i);
    }

    /*
     * capture the clip
     *
     * storage is allocated on the stack for the first 4 rects. This value was
     * chosen somewhat arbitrarily, but does allow us to represent simple clips
     * and some more common complex clips (e.g. a clipRect with a sub-rect
     * clipped out of its interior) without needing to malloc any additional memory.
     */
    SkSWriter32<4*sizeof(ClipRect)> clipWriter;

    if (!clip.isEmpty()) {
        // only returns the b/w clip so aa clips fail
        SkRegion::Iterator clip_iterator(clip);
        for (; !clip_iterator.done(); clip_iterator.next()) {
            // this assumes the SkIRect is stored in l,t,r,b ordering which
            // matches the ordering of our ClipRect struct
            clipWriter.writeIRect(clip_iterator.rect());
            state->clipRectCount++;
        }
    }

    // allocate memory for the clip then and copy them to the struct
    state->clipRects = (ClipRect*) sk_malloc_throw(clipWriter.bytesWritten());
    clipWriter.flatten(state->clipRects);
}
Esempio n. 4
0
void SkJSONCanvas::didConcat(const SkMatrix& matrix) {
    Json::Value command(Json::objectValue);
    switch (matrix.getType()) {
        case SkMatrix::kTranslate_Mask:
            command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_TRANSLATE);
            command[SKJSONCANVAS_ATTRIBUTE_X] = Json::Value(matrix.get(SkMatrix::kMTransX));
            command[SKJSONCANVAS_ATTRIBUTE_Y] = Json::Value(matrix.get(SkMatrix::kMTransY));
            break;
        case SkMatrix::kScale_Mask:
            command[SKJSONCANVAS_COMMAND] = Json::Value(SKJSONCANVAS_COMMAND_SCALE);
            command[SKJSONCANVAS_ATTRIBUTE_X] = Json::Value(matrix.get(SkMatrix::kMScaleX));
            command[SKJSONCANVAS_ATTRIBUTE_Y] = Json::Value(matrix.get(SkMatrix::kMScaleY));
            break;
        default:
            this->didSetMatrix(this->getTotalMatrix());
            return;
    }
    fCommands.append(command);
}
static bool are_equal(skiatest::Reporter* reporter,
                      const SkMatrix& a,
                      const SkMatrix& b) {
    bool equal = a == b;
    bool cheapEqual = a.cheapEqualTo(b);
    if (equal != cheapEqual) {
#ifdef SK_SCALAR_IS_FLOAT
        if (equal) {
            bool foundZeroSignDiff = false;
            for (int i = 0; i < 9; ++i) {
                float aVal = a.get(i);
                float bVal = b.get(i);
                int aValI = *SkTCast<int*>(&aVal);
                int bValI = *SkTCast<int*>(&bVal);
                if (0 == aVal && 0 == bVal && aValI != bValI) {
                    foundZeroSignDiff = true;
                } else {
                    REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
                }
            }
            REPORTER_ASSERT(reporter, foundZeroSignDiff);
        } else {
            bool foundNaN = false;
            for (int i = 0; i < 9; ++i) {
                float aVal = a.get(i);
                float bVal = b.get(i);
                int aValI = *SkTCast<int*>(&aVal);
                int bValI = *SkTCast<int*>(&bVal);
                if (sk_float_isnan(aVal) && aValI == bValI) {
                    foundNaN = true;
                } else {
                    REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
                }
            }
            REPORTER_ASSERT(reporter, foundNaN);
        }
#else
        REPORTER_ASSERT(reporter, false);
#endif
    }
    return equal;
}
Esempio n. 6
0
    static void getValues(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloatArray values) {
        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
        AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess);
        float* dst = autoValues.ptr();
#ifdef SK_SCALAR_IS_FLOAT
        for (int i = 0; i < 9; i++) {
            dst[i] = matrix->get(i);
        }
#else
        SkASSERT(false);
#endif
    }
Esempio n. 7
0
static void setSnapshotTransform(sp<SurfaceControl>& surfaceControl, SkMatrix &matrix, float alpha) {
    if (surfaceControl != NULL) {
#ifdef SK_SCALAR_IS_FIXED
        for (int i = 0; i < 6; i++) {
            tmpFloats[i] = SkFixedToFloat(matrix.get(i));
        }
        for (int j = 6; j < 9; j++) {
            tmpFloats[j] = SkFractToFloat(matrix.get(j));
        }
#else
        for (int i = 0; i < 9; i++) {
            tmpFloats[i] = matrix.get(i);
        }
#endif
        surfaceControl->setPosition(tmpFloats[MTRANS_X], tmpFloats[MTRANS_Y]);
        surfaceControl->setMatrix(tmpFloats[MSCALE_X], tmpFloats[MSKEW_Y],
                tmpFloats[MSKEW_X], tmpFloats[MSCALE_Y]);
//        LOGD("(trans_x,trans_y,scale_x,scale_y)=(%f,%f,%f,%f)",
//                tmpFloats[MSCALE_X], tmpFloats[MSKEW_Y],
//                tmpFloats[MSKEW_X], tmpFloats[MSCALE_Y]);
        surfaceControl->setAlpha(alpha);
    }
}
Esempio n. 8
0
void GrGLSLProgramDataManager::setSkMatrix(UniformHandle u, const SkMatrix& matrix) const {
    float mt[] = {
        matrix.get(SkMatrix::kMScaleX),
        matrix.get(SkMatrix::kMSkewY),
        matrix.get(SkMatrix::kMPersp0),
        matrix.get(SkMatrix::kMSkewX),
        matrix.get(SkMatrix::kMScaleY),
        matrix.get(SkMatrix::kMPersp1),
        matrix.get(SkMatrix::kMTransX),
        matrix.get(SkMatrix::kMTransY),
        matrix.get(SkMatrix::kMPersp2),
    };
    this->setMatrix3f(u, mt);
}
Esempio n. 9
0
void Matrix::NativeGetValues(
    /* [in] */ Int64 matrixHandle,
    /* [out] */ ArrayOf<Float>* values)
{
    SkASSERT(values->GetLength() >= 9);
    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
    // AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess);
    float* dst = values->GetPayload();
#ifdef SK_SCALAR_IS_FLOAT
    for (int i = 0; i < 9; i++) {
        dst[i] = matrix->get(i);
    }
#else
    SkASSERT(FALSE);
#endif
}
Esempio n. 10
0
static void android_view_MotionEvent_nativeTransform(JNIEnv* env, jclass clazz,
        jint nativePtr, jobject matrixObj) {
    SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);

    float m[9];
    m[0] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleX));
    m[1] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewX));
    m[2] = SkScalarToFloat(matrix->get(SkMatrix::kMTransX));
    m[3] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewY));
    m[4] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleY));
    m[5] = SkScalarToFloat(matrix->get(SkMatrix::kMTransY));
    m[6] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp0));
    m[7] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp1));
    m[8] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp2));
    event->transform(m);
}
SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
                                       const SkScalerContextEffects& effects,
                                       const SkDescriptor* desc)
    : SkScalerContext(typeface, effects, desc)
    , fTypeface(SkRef(typeface))
    , fGlyphCount(-1) {

#if SK_HAS_DWRITE_2_H
    fTypeface->fFactory->QueryInterface<IDWriteFactory2>(&fFactory2);

    SkTScopedComPtr<IDWriteFontFace2> fontFace2;
    fTypeface->fDWriteFontFace->QueryInterface<IDWriteFontFace2>(&fontFace2);
    fIsColorFont = fFactory2.get() && fontFace2.get() && fontFace2->IsColorFont();
#endif

    // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
    // except when bi-level rendering is requested or there are embedded
    // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
    //
    // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
    // this. As a result, determine the actual size of the text and then see if
    // there are any embedded bi-level bitmaps of that size. If there are, then
    // force bitmaps by requesting bi-level rendering.
    //
    // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
    // square pixels and only uses ppemY. Therefore the transform must track any
    // non-uniform x-scale.
    //
    // Also, rotated glyphs should have the same absolute advance widths as
    // horizontal glyphs and the subpixel flag should not affect glyph shapes.

    SkVector scale;
    SkMatrix GsA;
    fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
                         &scale, &fSkXform, &GsA, &fG_inv);

    fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
    fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
    fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
    fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
    fXform.dx = 0;
    fXform.dy = 0;

    fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
    fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
    fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
    fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
    fGsA.dx = 0;
    fGsA.dy = 0;

    // realTextSize is the actual device size we want (as opposed to the size the user requested).
    // gdiTextSize is the size we request when GDI compatible.
    // If the scale is negative, this means the matrix will do the flip anyway.
    const SkScalar realTextSize = scale.fY;
    // Due to floating point math, the lower bits are suspect. Round carefully.
    SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
    if (gdiTextSize == 0) {
        gdiTextSize = SK_Scalar1;
    }

    bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
    bool treatLikeBitmap = false;
    bool axisAlignedBitmap = false;
    if (bitmapRequested) {
        // When embedded bitmaps are requested, treat the entire range like
        // a bitmap strike if the range is gridfit only and contains a bitmap.
        int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
        PPEMRange range = { bitmapPPEM, bitmapPPEM };
        expand_range_if_gridfit_only(typeface, bitmapPPEM, &range);
        treatLikeBitmap = has_bitmap_strike(typeface, range);

        axisAlignedBitmap = is_axis_aligned(fRec);
    }

    // If the user requested aliased, do so with aliased compatible metrics.
    if (SkMask::kBW_Format == fRec.fMaskFormat) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
        fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

        // If we can use a bitmap, use gdi classic rendering and measurement.
        // This will not always provide a bitmap, but matches expected behavior.
    } else if (treatLikeBitmap && axisAlignedBitmap) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

        // If rotated but the horizontal text could have used a bitmap,
        // render high quality rotated glyphs but measure using bitmap metrics.
    } else if (treatLikeBitmap) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

        // Fonts that have hints but no gasp table get non-symmetric rendering.
        // Usually such fonts have low quality hints which were never tested
        // with anything but GDI ClearType classic. Such fonts often rely on
        // drop out control in the y direction in order to be legible.
    } else if (is_hinted_without_gasp(typeface)) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;

        // The normal case is to use natural symmetric rendering and linear metrics.
    } else {
        fTextSizeRender = realTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    }

    if (this->isSubpixel()) {
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    }
}
Esempio n. 12
0
SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
                                       const SkDescriptor* desc)
        : SkScalerContext(typeface, desc)
        , fTypeface(SkRef(typeface))
        , fGlyphCount(-1) {

    // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
    // except when bi-level rendering is requested or there are embedded
    // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
    //
    // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
    // this. As a result, determine the actual size of the text and then see if
    // there are any embedded bi-level bitmaps of that size. If there are, then
    // force bitmaps by requesting bi-level rendering.
    //
    // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
    // square pixels and only uses ppemY. Therefore the transform must track any
    // non-uniform x-scale.
    //
    // Also, rotated glyphs should have the same absolute advance widths as
    // horizontal glyphs and the subpixel flag should not affect glyph shapes.

    // A is the total matrix.
    SkMatrix A;
    fRec.getSingleMatrix(&A);

    // h is where A maps the horizontal baseline.
    SkPoint h = SkPoint::Make(SK_Scalar1, 0);
    A.mapPoints(&h, 1);

    // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
    SkMatrix G;
    SkComputeGivensRotation(h, &G);

    // GA is the matrix A with rotation removed.
    SkMatrix GA(G);
    GA.preConcat(A);

    // realTextSize is the actual device size we want (as opposed to the size the user requested).
    // gdiTextSize is the size we request when GDI compatible.
    // If the scale is negative, this means the matrix will do the flip anyway.
    SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
    // Due to floating point math, the lower bits are suspect. Round carefully.
    SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
    if (gdiTextSize == 0) {
        gdiTextSize = SK_Scalar1;
    }

    bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
    bool treatLikeBitmap = false;
    bool axisAlignedBitmap = false;
    if (bitmapRequested) {
        // When embedded bitmaps are requested, treat the entire range like
        // a bitmap strike if the range is gridfit only and contains a bitmap.
        int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
        PPEMRange range = { bitmapPPEM, bitmapPPEM };
        expand_range_if_gridfit_only(typeface, bitmapPPEM, &range);
        treatLikeBitmap = has_bitmap_strike(typeface, range);

        axisAlignedBitmap = is_axis_aligned(fRec);
    }

    // If the user requested aliased, do so with aliased compatible metrics.
    if (SkMask::kBW_Format == fRec.fMaskFormat) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
        fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

    // If we can use a bitmap, use gdi classic rendering and measurement.
    // This will not always provide a bitmap, but matches expected behavior.
    } else if (treatLikeBitmap && axisAlignedBitmap) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

    // If rotated but the horizontal text could have used a bitmap,
    // render high quality rotated glyphs but measure using bitmap metrics.
    } else if (treatLikeBitmap) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

    // Fonts that have hints but no gasp table get non-symmetric rendering.
    // Usually such fonts have low quality hints which were never tested
    // with anything but GDI ClearType classic. Such fonts often rely on
    // drop out control in the y direction in order to be legible.
    } else if (is_hinted_without_gasp(typeface)) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;

    // The normal case is to use natural symmetric rendering and linear metrics.
    } else {
        fTextSizeRender = realTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    }

    if (this->isSubpixel()) {
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    }

    // Remove the realTextSize, as that is the text height scale currently in A.
    SkScalar scale = SkScalarInvert(realTextSize);

    // fSkXform is the total matrix A without the text height scale.
    fSkXform = A;
    fSkXform.preScale(scale, scale); //remove the text height scale.

    fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
    fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
    fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
    fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
    fXform.dx = 0;
    fXform.dy = 0;

    // GsA is the non-rotational part of A without the text height scale.
    SkMatrix GsA(GA);
    GsA.preScale(scale, scale); //remove text height scale, G is rotational so reorders with scale.

    fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
    fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
    fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
    fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
    fGsA.dx = 0;
    fGsA.dy = 0;

    // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational.
    fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
                  -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
                  G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
}
Esempio n. 13
0
void SkScalerContextRec::computeMatrices(PreMatrixScale preMatrixScale, SkVector* s, SkMatrix* sA,
                                         SkMatrix* GsA, SkMatrix* G_inv, SkMatrix* A_out)
{
    // A is the 'total' matrix.
    SkMatrix A;
    this->getSingleMatrix(&A);

    // The caller may find the 'total' matrix useful when dealing directly with EM sizes.
    if (A_out) {
        *A_out = A;
    }

    // If the 'total' matrix is singular, set the 'scale' to something finite and zero the matrices.
    // All underlying ports have issues with zero text size, so use the matricies to zero.

    // Map the vectors [0,1], [1,0], [1,1] and [1,-1] (the EM) through the 'total' matrix.
    // If the length of one of these vectors is less than 1/256 then an EM filling square will
    // never affect any pixels.
    SkVector diag[4] = { { A.getScaleX()               ,                 A.getSkewY() },
                         {                 A.getSkewX(), A.getScaleY()                },
                         { A.getScaleX() + A.getSkewX(), A.getScaleY() + A.getSkewY() },
                         { A.getScaleX() - A.getSkewX(), A.getScaleY() - A.getSkewY() }, };
    if (diag[0].lengthSqd() <= SK_ScalarNearlyZero * SK_ScalarNearlyZero ||
        diag[1].lengthSqd() <= SK_ScalarNearlyZero * SK_ScalarNearlyZero ||
        diag[2].lengthSqd() <= SK_ScalarNearlyZero * SK_ScalarNearlyZero ||
        diag[3].lengthSqd() <= SK_ScalarNearlyZero * SK_ScalarNearlyZero)
    {
        s->fX = SK_Scalar1;
        s->fY = SK_Scalar1;
        sA->setScale(0, 0);
        if (GsA) {
            GsA->setScale(0, 0);
        }
        if (G_inv) {
            G_inv->reset();
        }
        return;
    }

    // GA is the matrix A with rotation removed.
    SkMatrix GA;
    bool skewedOrFlipped = A.getSkewX() || A.getSkewY() || A.getScaleX() < 0 || A.getScaleY() < 0;
    if (skewedOrFlipped) {
        // h is where A maps the horizontal baseline.
        SkPoint h = SkPoint::Make(SK_Scalar1, 0);
        A.mapPoints(&h, 1);

        // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
        SkMatrix G;
        SkComputeGivensRotation(h, &G);

        GA = G;
        GA.preConcat(A);

        // The 'remainingRotation' is G inverse, which is fairly simple since G is 2x2 rotational.
        if (G_inv) {
            G_inv->setAll(
                G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
                -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
                G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
        }
    } else {
        GA = A;
        if (G_inv) {
            G_inv->reset();
        }
    }

    // At this point, given GA, create s.
    switch (preMatrixScale) {
        case kFull_PreMatrixScale:
            s->fX = SkScalarAbs(GA.get(SkMatrix::kMScaleX));
            s->fY = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
            break;
        case kVertical_PreMatrixScale: {
            SkScalar yScale = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
            s->fX = yScale;
            s->fY = yScale;
            break;
        }
        case kVerticalInteger_PreMatrixScale: {
            SkScalar realYScale = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
            SkScalar intYScale = SkScalarRoundToScalar(realYScale);
            if (intYScale == 0) {
                intYScale = SK_Scalar1;
            }
            s->fX = intYScale;
            s->fY = intYScale;
            break;
        }
    }

    // The 'remaining' matrix sA is the total matrix A without the scale.
    if (!skewedOrFlipped && (
            (kFull_PreMatrixScale == preMatrixScale) ||
            (kVertical_PreMatrixScale == preMatrixScale && A.getScaleX() == A.getScaleY())))
    {
        // If GA == A and kFull_PreMatrixScale, sA is identity.
        // If GA == A and kVertical_PreMatrixScale and A.scaleX == A.scaleY, sA is identity.
        sA->reset();
    } else if (!skewedOrFlipped && kVertical_PreMatrixScale == preMatrixScale) {
        // If GA == A and kVertical_PreMatrixScale, sA.scaleY is SK_Scalar1.
        sA->reset();
        sA->setScaleX(A.getScaleX() / s->fY);
    } else {
        // TODO: like kVertical_PreMatrixScale, kVerticalInteger_PreMatrixScale with int scales.
        *sA = A;
        sA->preScale(SkScalarInvert(s->fX), SkScalarInvert(s->fY));
    }

    // The 'remainingWithoutRotation' matrix GsA is the non-rotational part of A without the scale.
    if (GsA) {
        *GsA = GA;
         // G is rotational so reorders with the scale.
        GsA->preScale(SkScalarInvert(s->fX), SkScalarInvert(s->fY));
    }
}
Esempio n. 14
0
void GrPathUtils::QuadUVMatrix::set(const SkPoint qPts[3]) {
    SkMatrix m;
    // We want M such that M * xy_pt = uv_pt
    // We know M * control_pts = [0  1/2 1]
    //                           [0  0   1]
    //                           [1  1   1]
    // And control_pts = [x0 x1 x2]
    //                   [y0 y1 y2]
    //                   [1  1  1 ]
    // We invert the control pt matrix and post concat to both sides to get M.
    // Using the known form of the control point matrix and the result, we can
    // optimize and improve precision.

    double x0 = qPts[0].fX;
    double y0 = qPts[0].fY;
    double x1 = qPts[1].fX;
    double y1 = qPts[1].fY;
    double x2 = qPts[2].fX;
    double y2 = qPts[2].fY;
    double det = x0*y1 - y0*x1 + x2*y0 - y2*x0 + x1*y2 - y1*x2;

    if (!sk_float_isfinite(det)
        || SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
        // The quad is degenerate. Hopefully this is rare. Find the pts that are
        // farthest apart to compute a line (unless it is really a pt).
        SkScalar maxD = qPts[0].distanceToSqd(qPts[1]);
        int maxEdge = 0;
        SkScalar d = qPts[1].distanceToSqd(qPts[2]);
        if (d > maxD) {
            maxD = d;
            maxEdge = 1;
        }
        d = qPts[2].distanceToSqd(qPts[0]);
        if (d > maxD) {
            maxD = d;
            maxEdge = 2;
        }
        // We could have a tolerance here, not sure if it would improve anything
        if (maxD > 0) {
            // Set the matrix to give (u = 0, v = distance_to_line)
            SkVector lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge];
            // when looking from the point 0 down the line we want positive
            // distances to be to the left. This matches the non-degenerate
            // case.
            lineVec.setOrthog(lineVec, SkPoint::kLeft_Side);
            // first row
            fM[0] = 0;
            fM[1] = 0;
            fM[2] = 0;
            // second row
            fM[3] = lineVec.fX;
            fM[4] = lineVec.fY;
            fM[5] = -lineVec.dot(qPts[maxEdge]);
        } else {
            // It's a point. It should cover zero area. Just set the matrix such
            // that (u, v) will always be far away from the quad.
            fM[0] = 0; fM[1] = 0; fM[2] = 100.f;
            fM[3] = 0; fM[4] = 0; fM[5] = 100.f;
        }
    } else {
        double scale = 1.0/det;

        // compute adjugate matrix
        double a2, a3, a4, a5, a6, a7, a8;
        a2 = x1*y2-x2*y1;

        a3 = y2-y0;
        a4 = x0-x2;
        a5 = x2*y0-x0*y2;

        a6 = y0-y1;
        a7 = x1-x0;
        a8 = x0*y1-x1*y0;

        // this performs the uv_pts*adjugate(control_pts) multiply,
        // then does the scale by 1/det afterwards to improve precision
        m[SkMatrix::kMScaleX] = (float)((0.5*a3 + a6)*scale);
        m[SkMatrix::kMSkewX]  = (float)((0.5*a4 + a7)*scale);
        m[SkMatrix::kMTransX] = (float)((0.5*a5 + a8)*scale);

        m[SkMatrix::kMSkewY]  = (float)(a6*scale);
        m[SkMatrix::kMScaleY] = (float)(a7*scale);
        m[SkMatrix::kMTransY] = (float)(a8*scale);

        // kMPersp0 & kMPersp1 should algebraically be zero
        m[SkMatrix::kMPersp0] = 0.0f;
        m[SkMatrix::kMPersp1] = 0.0f;
        m[SkMatrix::kMPersp2] = (float)((a2 + a5 + a8)*scale);

        // It may not be normalized to have 1.0 in the bottom right
        float m33 = m.get(SkMatrix::kMPersp2);
        if (1.f != m33) {
            m33 = 1.f / m33;
            fM[0] = m33 * m.get(SkMatrix::kMScaleX);
            fM[1] = m33 * m.get(SkMatrix::kMSkewX);
            fM[2] = m33 * m.get(SkMatrix::kMTransX);
            fM[3] = m33 * m.get(SkMatrix::kMSkewY);
            fM[4] = m33 * m.get(SkMatrix::kMScaleY);
            fM[5] = m33 * m.get(SkMatrix::kMTransY);
        } else {
            fM[0] = m.get(SkMatrix::kMScaleX);
            fM[1] = m.get(SkMatrix::kMSkewX);
            fM[2] = m.get(SkMatrix::kMTransX);
            fM[3] = m.get(SkMatrix::kMSkewY);
            fM[4] = m.get(SkMatrix::kMScaleY);
            fM[5] = m.get(SkMatrix::kMTransY);
        }
    }
}
void GrPathUtils::QuadUVMatrix::set(const GrPoint qPts[3]) {
    // can't make this static, no cons :(
    SkMatrix UVpts;
#ifndef SK_SCALAR_IS_FLOAT
    GrCrash("Expected scalar is float.");
#endif
    SkMatrix m;
    // We want M such that M * xy_pt = uv_pt
    // We know M * control_pts = [0  1/2 1]
    //                           [0  0   1]
    //                           [1  1   1]
    // We invert the control pt matrix and post concat to both sides to get M.
    UVpts.setAll(0,   SK_ScalarHalf,  SK_Scalar1,
                 0,               0,  SK_Scalar1,
                 SkScalarToPersp(SK_Scalar1),
                 SkScalarToPersp(SK_Scalar1),
                 SkScalarToPersp(SK_Scalar1));
    m.setAll(qPts[0].fX, qPts[1].fX, qPts[2].fX,
             qPts[0].fY, qPts[1].fY, qPts[2].fY,
             SkScalarToPersp(SK_Scalar1),
             SkScalarToPersp(SK_Scalar1),
             SkScalarToPersp(SK_Scalar1));
    if (!m.invert(&m)) {
        // The quad is degenerate. Hopefully this is rare. Find the pts that are
        // farthest apart to compute a line (unless it is really a pt).
        SkScalar maxD = qPts[0].distanceToSqd(qPts[1]);
        int maxEdge = 0;
        SkScalar d = qPts[1].distanceToSqd(qPts[2]);
        if (d > maxD) {
            maxD = d;
            maxEdge = 1;
        }
        d = qPts[2].distanceToSqd(qPts[0]);
        if (d > maxD) {
            maxD = d;
            maxEdge = 2;
        }
        // We could have a tolerance here, not sure if it would improve anything
        if (maxD > 0) {
            // Set the matrix to give (u = 0, v = distance_to_line)
            GrVec lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge];
            // when looking from the point 0 down the line we want positive
            // distances to be to the left. This matches the non-degenerate
            // case.
            lineVec.setOrthog(lineVec, GrPoint::kLeft_Side);
            lineVec.dot(qPts[0]);
            // first row
            fM[0] = 0;
            fM[1] = 0;
            fM[2] = 0;
            // second row
            fM[3] = lineVec.fX;
            fM[4] = lineVec.fY;
            fM[5] = -lineVec.dot(qPts[maxEdge]);
        } else {
            // It's a point. It should cover zero area. Just set the matrix such
            // that (u, v) will always be far away from the quad.
            fM[0] = 0; fM[1] = 0; fM[2] = 100.f;
            fM[3] = 0; fM[4] = 0; fM[5] = 100.f;
        }
    } else {
        m.postConcat(UVpts);

        // The matrix should not have perspective.
        SkDEBUGCODE(static const SkScalar gTOL = SkFloatToScalar(1.f / 100.f));
        GrAssert(SkScalarAbs(m.get(SkMatrix::kMPersp0)) < gTOL);
        GrAssert(SkScalarAbs(m.get(SkMatrix::kMPersp1)) < gTOL);

        // It may not be normalized to have 1.0 in the bottom right
        float m33 = m.get(SkMatrix::kMPersp2);
        if (1.f != m33) {
            m33 = 1.f / m33;
            fM[0] = m33 * m.get(SkMatrix::kMScaleX);
            fM[1] = m33 * m.get(SkMatrix::kMSkewX);
            fM[2] = m33 * m.get(SkMatrix::kMTransX);
            fM[3] = m33 * m.get(SkMatrix::kMSkewY);
            fM[4] = m33 * m.get(SkMatrix::kMScaleY);
            fM[5] = m33 * m.get(SkMatrix::kMTransY);
        } else {
            fM[0] = m.get(SkMatrix::kMScaleX);
            fM[1] = m.get(SkMatrix::kMSkewX);
            fM[2] = m.get(SkMatrix::kMTransX);
            fM[3] = m.get(SkMatrix::kMSkewY);
            fM[4] = m.get(SkMatrix::kMScaleY);
            fM[5] = m.get(SkMatrix::kMTransY);
        }
    }
}