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; }
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); }
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; }
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 }
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); } }
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); }
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 }
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; } }
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)); }
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)); } }
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); } } }