static void test_matrix_recttorect(skiatest::Reporter* reporter) { SkRect src, dst; SkMatrix matrix; src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10); dst = src; matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType()); REPORTER_ASSERT(reporter, matrix.rectStaysRect()); dst.offset(SK_Scalar1, SK_Scalar1); matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType()); REPORTER_ASSERT(reporter, matrix.rectStaysRect()); dst.fRight += SK_Scalar1; matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); REPORTER_ASSERT(reporter, (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType()); REPORTER_ASSERT(reporter, matrix.rectStaysRect()); dst = src; dst.fRight = src.fRight * 2; matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit); REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType()); REPORTER_ASSERT(reporter, matrix.rectStaysRect()); }
bool shouldDrawAntiAliased(const GraphicsContext* context, const SkRect& destRect) { if (!context->shouldAntialias()) return false; const SkMatrix totalMatrix = context->getTotalMatrix(); // Don't disable anti-aliasing if we're rotated or skewed. if (!totalMatrix.rectStaysRect()) return true; // Disable anti-aliasing for scales or n*90 degree rotations. // Allow to opt out of the optimization though for "hairline" geometry // images - using the shouldAntialiasHairlineImages() GraphicsContext flag. if (!context->shouldAntialiasHairlineImages()) return false; // Check if the dimensions of the destination are "small" (less than one // device pixel). To prevent sudden drop-outs. Since we know that // kRectStaysRect_Mask is set, the matrix either has scale and no skew or // vice versa. We can query the kAffine_Mask flag to determine which case // it is. // FIXME: This queries the CTM while drawing, which is generally // discouraged. Always drawing with AA can negatively impact performance // though - that's why it's not always on. SkScalar widthExpansion, heightExpansion; if (totalMatrix.getType() & SkMatrix::kAffine_Mask) widthExpansion = totalMatrix[SkMatrix::kMSkewY], heightExpansion = totalMatrix[SkMatrix::kMSkewX]; else widthExpansion = totalMatrix[SkMatrix::kMScaleX], heightExpansion = totalMatrix[SkMatrix::kMScaleY]; return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fabs(heightExpansion) < 1; }
void Matrix4::load(const SkMatrix& v) { memset(data, 0, sizeof(data)); data[kScaleX] = v[SkMatrix::kMScaleX]; data[kSkewX] = v[SkMatrix::kMSkewX]; data[kTranslateX] = v[SkMatrix::kMTransX]; data[kSkewY] = v[SkMatrix::kMSkewY]; data[kScaleY] = v[SkMatrix::kMScaleY]; data[kTranslateY] = v[SkMatrix::kMTransY]; data[kPerspective0] = v[SkMatrix::kMPersp0]; data[kPerspective1] = v[SkMatrix::kMPersp1]; data[kPerspective2] = v[SkMatrix::kMPersp2]; data[kScaleZ] = 1.0f; // NOTE: The flags are compatible between SkMatrix and this class. // However, SkMatrix::getType() does not return the flag // kRectStaysRect. The return value is masked with 0xF // so we need the extra rectStaysRect() check mType = v.getType(); if (v.rectStaysRect()) { mType |= kTypeRectToRect; } }
bool GrBicubicEffect::ShouldUseBicubic(const SkMatrix& matrix, GrTextureParams::FilterMode* filterMode) { if (matrix.isIdentity()) { *filterMode = GrTextureParams::kNone_FilterMode; return false; } SkScalar scales[2]; if (!matrix.getMinMaxScales(scales) || scales[0] < SK_Scalar1) { // Bicubic doesn't handle arbitrary minimization well, as src texels can be skipped // entirely, *filterMode = GrTextureParams::kMipMap_FilterMode; return false; } // At this point if scales[1] == SK_Scalar1 then the matrix doesn't do any scaling. if (scales[1] == SK_Scalar1) { if (matrix.rectStaysRect() && SkScalarIsInt(matrix.getTranslateX()) && SkScalarIsInt(matrix.getTranslateY())) { *filterMode = GrTextureParams::kNone_FilterMode; } else { // Use bilerp to handle rotation or fractional translation. *filterMode = GrTextureParams::kBilerp_FilterMode; } return false; } // When we use the bicubic filtering effect each sample is read from the texture using // nearest neighbor sampling. *filterMode = GrTextureParams::kNone_FilterMode; return true; }
void onDraw(SkCanvas* canvas) override { static const int kOn = 4; static const int kOff = 4; static const int kIntervalLength = kOn + kOff; static const SkColor gColors[kIntervalLength] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorYELLOW, SK_ColorGRAY, SK_ColorDKGRAY }; SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setAntiAlias(fDoAA); SkMatrix rot; rot.setRotate(90); SkASSERT(rot.rectStaysRect()); canvas->concat(rot); int sign; // used to toggle the direction of the lines int phase = 0; for (int x = 0; x < 200; x += 10) { paint.setStrokeWidth(SkIntToScalar(phase+1)); paint.setColor(gColors[phase]); sign = (x % 20) ? 1 : -1; drawline(canvas, kOn, kOff, paint, SkIntToScalar(x), -sign * SkIntToScalar(10003), SkIntToScalar(phase), SkIntToScalar(x), sign * SkIntToScalar(10003)); phase = (phase + 1) % kIntervalLength; } for (int y = -400; y < 0; y += 10) { paint.setStrokeWidth(SkIntToScalar(phase+1)); paint.setColor(gColors[phase]); sign = (y % 20) ? 1 : -1; drawline(canvas, kOn, kOff, paint, -sign * SkIntToScalar(10003), SkIntToScalar(y), SkIntToScalar(phase), sign * SkIntToScalar(10003), SkIntToScalar(y)); phase = (phase + 1) % kIntervalLength; } }
static bool can_ignore_bilerp_constraint(const GrTextureProducer& producer, const SkRect& srcRect, const SkMatrix& srcRectToDeviceSpace, bool isMSAA) { if (srcRectToDeviceSpace.rectStaysRect()) { // sampling is axis-aligned SkRect transformedRect; srcRectToDeviceSpace.mapRect(&transformedRect, srcRect); if (has_aligned_samples(srcRect, transformedRect) || !may_color_bleed(srcRect, transformedRect, srcRectToDeviceSpace, isMSAA)) { return true; } } return false; }
void GrAARectRenderer::FillAANestedRects(GrDrawTarget* target, const GrPipelineBuilder& pipelineBuilder, GrColor color, const SkMatrix& viewMatrix, const SkRect rects[2]) { SkASSERT(viewMatrix.rectStaysRect()); SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty()); SkRect devOutside, devInside; viewMatrix.mapRect(&devOutside, rects[0]); viewMatrix.mapRect(&devInside, rects[1]); if (devInside.isEmpty()) { FillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside); return; } GeometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside, devInside, true); }
void GrAARectRenderer::fillAANestedRects(GrDrawTarget* target, GrDrawState* drawState, GrColor color, const SkRect rects[2], const SkMatrix& combinedMatrix) { SkASSERT(combinedMatrix.rectStaysRect()); SkASSERT(!rects[1].isEmpty()); SkRect devOutside, devOutsideAssist, devInside; combinedMatrix.mapRect(&devOutside, rects[0]); // can't call mapRect for devInside since it calls sort combinedMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2); if (devInside.isEmpty()) { this->fillAARect(target, drawState, color, devOutside, SkMatrix::I(), devOutside); return; } this->geometryStrokeAARect(target, drawState, color, devOutside, devOutsideAssist, devInside, true); }
static bool apply_aa_to_rect(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder, SkRect* devBoundRect, const SkRect& rect, SkScalar strokeWidth, const SkMatrix& combinedMatrix, GrColor color) { if (pipelineBuilder->getRenderTarget()->isUnifiedMultisampled() || !combinedMatrix.preservesAxisAlignment()) { return false; } combinedMatrix.mapRect(devBoundRect, rect); if (!combinedMatrix.rectStaysRect()) { return true; } if (strokeWidth < 0) { return !is_irect(*devBoundRect); } return true; }
static bool apply_aa_to_rect(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder, SkRect* devBoundRect, const SkRect& rect, SkScalar strokeWidth, const SkMatrix& combinedMatrix, GrColor color) { if (pipelineBuilder->getRenderTarget()->isUnifiedMultisampled()) { return false; } #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) if (strokeWidth >= 0) { #endif if (!combinedMatrix.preservesAxisAlignment()) { return false; } #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) } else { if (!combinedMatrix.preservesRightAngles()) { return false; } } #endif combinedMatrix.mapRect(devBoundRect, rect); if (!combinedMatrix.rectStaysRect()) { return true; } if (strokeWidth < 0) { return !is_irect(*devBoundRect); } return true; }
// 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; }
// Currently asPoints is more restrictive then it needs to be. In the future // we need to: // allow kRound_Cap capping (could allow rotations in the matrix with this) // allow paths to be returned bool SkDashPathEffect::asPoints(PointData* results, const SkPath& src, const SkStrokeRec& rec, const SkMatrix& matrix, const SkRect* cullRect) const { // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out if (fInitialDashLength < 0 || 0 >= rec.getWidth()) { return false; } // TODO: this next test could be eased up. We could allow any number of // intervals as long as all the ons match and all the offs match. // Additionally, they do not necessarily need to be integers. // We cannot allow arbitrary intervals since we want the returned points // to be uniformly sized. if (fCount != 2 || !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) || !SkScalarIsInt(fIntervals[0]) || !SkScalarIsInt(fIntervals[1])) { return false; } SkPoint pts[2]; if (!src.isLine(pts)) { return false; } // TODO: this test could be eased up to allow circles if (SkPaint::kButt_Cap != rec.getCap()) { return false; } // TODO: this test could be eased up for circles. Rotations could be allowed. if (!matrix.rectStaysRect()) { return false; } // See if the line can be limited to something plausible. if (!cull_line(pts, rec, matrix, cullRect, fIntervalLength)) { return false; } SkScalar length = SkPoint::Distance(pts[1], pts[0]); SkVector tangent = pts[1] - pts[0]; if (tangent.isZero()) { return false; } tangent.scale(SkScalarInvert(length)); // TODO: make this test for horizontal & vertical lines more robust bool isXAxis = true; if (SkScalarNearlyEqual(SK_Scalar1, tangent.fX) || SkScalarNearlyEqual(-SK_Scalar1, tangent.fX)) { results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth())); } else if (SkScalarNearlyEqual(SK_Scalar1, tangent.fY) || SkScalarNearlyEqual(-SK_Scalar1, tangent.fY)) { results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0])); isXAxis = false; } else if (SkPaint::kRound_Cap != rec.getCap()) { // Angled lines don't have axis-aligned boxes. return false; } if (results) { results->fFlags = 0; SkScalar clampedInitialDashLength = SkMinScalar(length, fInitialDashLength); if (SkPaint::kRound_Cap == rec.getCap()) { results->fFlags |= PointData::kCircles_PointFlag; } results->fNumPoints = 0; SkScalar len2 = length; if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { SkASSERT(len2 >= clampedInitialDashLength); if (0 == fInitialDashIndex) { if (clampedInitialDashLength > 0) { if (clampedInitialDashLength >= fIntervals[0]) { ++results->fNumPoints; // partial first dash } len2 -= clampedInitialDashLength; } len2 -= fIntervals[1]; // also skip first space if (len2 < 0) { len2 = 0; } } else { len2 -= clampedInitialDashLength; // skip initial partial empty } } int numMidPoints = SkScalarFloorToInt(len2 / fIntervalLength); results->fNumPoints += numMidPoints; len2 -= numMidPoints * fIntervalLength; bool partialLast = false; if (len2 > 0) { if (len2 < fIntervals[0]) { partialLast = true; } else { ++numMidPoints; ++results->fNumPoints; } } results->fPoints = new SkPoint[results->fNumPoints]; SkScalar distance = 0; int curPt = 0; if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { SkASSERT(clampedInitialDashLength <= length); if (0 == fInitialDashIndex) { if (clampedInitialDashLength > 0) { // partial first block SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, SkScalarHalf(clampedInitialDashLength)); SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, SkScalarHalf(clampedInitialDashLength)); SkScalar halfWidth, halfHeight; if (isXAxis) { halfWidth = SkScalarHalf(clampedInitialDashLength); halfHeight = SkScalarHalf(rec.getWidth()); } else { halfWidth = SkScalarHalf(rec.getWidth()); halfHeight = SkScalarHalf(clampedInitialDashLength); } if (clampedInitialDashLength < fIntervals[0]) { // This one will not be like the others results->fFirst.addRect(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight); } else { SkASSERT(curPt < results->fNumPoints); results->fPoints[curPt].set(x, y); ++curPt; } distance += clampedInitialDashLength; } distance += fIntervals[1]; // skip over the next blank block too } else { distance += clampedInitialDashLength; } } if (0 != numMidPoints) { distance += SkScalarHalf(fIntervals[0]); for (int i = 0; i < numMidPoints; ++i) { SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance); SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance); SkASSERT(curPt < results->fNumPoints); results->fPoints[curPt].set(x, y); ++curPt; distance += fIntervalLength; } distance -= SkScalarHalf(fIntervals[0]); } if (partialLast) { // partial final block SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles SkScalar temp = length - distance; SkASSERT(temp < fIntervals[0]); SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance + SkScalarHalf(temp)); SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance + SkScalarHalf(temp)); SkScalar halfWidth, halfHeight; if (isXAxis) { halfWidth = SkScalarHalf(temp); halfHeight = SkScalarHalf(rec.getWidth()); } else { halfWidth = SkScalarHalf(rec.getWidth()); halfHeight = SkScalarHalf(temp); } results->fLast.addRect(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight); } SkASSERT(curPt == results->fNumPoints); } return true; }
static jboolean rectStaysRect(JNIEnv* env, jobject clazz, jlong objHandle) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE; }
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); } } }
DEF_TEST(Matrix, reporter) { SkMatrix mat, inverse, iden1, iden2; mat.reset(); mat.setTranslate(SK_Scalar1, SK_Scalar1); REPORTER_ASSERT(reporter, mat.invert(&inverse)); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); mat.setScale(SkIntToScalar(2), SkIntToScalar(4)); REPORTER_ASSERT(reporter, mat.invert(&inverse)); iden1.setConcat(mat, inverse); REPORTER_ASSERT(reporter, is_identity(iden1)); test_flatten(reporter, mat); mat.setScale(SK_Scalar1/2, SkIntToScalar(2)); REPORTER_ASSERT(reporter, 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)); REPORTER_ASSERT(reporter, 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); mat.setScale(0, SK_Scalar1); REPORTER_ASSERT(reporter, !mat.invert(NULL)); REPORTER_ASSERT(reporter, !mat.invert(&inverse)); mat.setScale(SK_Scalar1, 0); REPORTER_ASSERT(reporter, !mat.invert(NULL)); REPORTER_ASSERT(reporter, !mat.invert(&inverse)); // 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(); mat.reset(); mat.set(SkMatrix::kMSkewX, SK_ScalarNaN); mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN); REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2)); test_matrix_min_max_scale(reporter); test_matrix_is_similarity(reporter); test_matrix_recttorect(reporter); test_matrix_decomposition(reporter); test_matrix_homogeneous(reporter); }
void GrAARectRenderer::geometryFillAARect(GrDrawTarget* target, GrDrawState* drawState, GrColor color, const SkRect& rect, const SkMatrix& combinedMatrix, const SkRect& devRect) { GrDrawState::AutoRestoreEffects are(drawState); CoverageAttribType type; SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(*drawState, color, &type)); size_t vertexStride = gp->getVertexStride(); GrDrawTarget::AutoReleaseGeometry geo(target, 8, vertexStride, 0); if (!geo.succeeded()) { SkDebugf("Failed to get space for vertices!\n"); return; } if (NULL == fAAFillRectIndexBuffer) { fAAFillRectIndexBuffer = fGpu->createInstancedIndexBuffer(gFillAARectIdx, kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer, kVertsPerAAFillRect); } GrIndexBuffer* indexBuffer = fAAFillRectIndexBuffer; if (NULL == indexBuffer) { SkDebugf("Failed to create index buffer!\n"); return; } intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices()); SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride); SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1); inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height()); if (combinedMatrix.rectStaysRect()) { // Temporarily #if'ed out. We don't want to pass in the devRect but // right now it is computed in GrContext::apply_aa_to_rect and we don't // want to throw away the work #if 0 SkRect devRect; combinedMatrix.mapRect(&devRect, rect); #endif set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf); set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset); } else { // compute transformed (1, 0) and (0, 1) vectors SkVector vec[2] = { { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] }, { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] } }; vec[0].normalize(); vec[0].scale(SK_ScalarHalf); vec[1].normalize(); vec[1].scale(SK_ScalarHalf); // create the rotated rect fan0Pos->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); combinedMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4); // Now create the inset points and then outset the original // rotated points // TL *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1]; // BL *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1]; // BR *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1]; // TR *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1]; } // Make verts point to vertex color and then set all the color and coverage vertex attrs values. verts += sizeof(SkPoint); for (int i = 0; i < 4; ++i) { if (kUseCoverage_CoverageAttribType == type) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; } } int scale; if (inset < SK_ScalarHalf) { scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); SkASSERT(scale >= 0 && scale <= 255); } else { scale = 0xff; } verts += 4 * vertexStride; float innerCoverage = GrNormalizeByteToFloat(scale); GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); for (int i = 0; i < 4; ++i) { if (kUseCoverage_CoverageAttribType == type) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; } } target->setIndexSourceToBuffer(indexBuffer); target->drawIndexedInstances(drawState, gp, kTriangles_GrPrimitiveType, 1, kVertsPerAAFillRect, kIndicesPerAAFillRect); target->resetIndexSource(); }
static void generate_aa_fill_rect_geometry(intptr_t verts, size_t vertexStride, GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect& devRect, bool tweakAlphaForCoverage, const SkMatrix* localMatrix) { SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride); SkScalar inset; if (viewMatrix.rectStaysRect()) { inset = SkMinScalar(devRect.width(), SK_Scalar1); inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height()); set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf); set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset); } else { // compute transformed (1, 0) and (0, 1) vectors SkVector vec[2] = {{viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY]}, {viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY]}}; SkScalar len1 = SkPoint::Normalize(&vec[0]); vec[0].scale(SK_ScalarHalf); SkScalar len2 = SkPoint::Normalize(&vec[1]); vec[1].scale(SK_ScalarHalf); inset = SkMinScalar(len1 * rect.width(), SK_Scalar1); inset = SK_ScalarHalf * SkMinScalar(inset, len2 * rect.height()); // create the rotated rect SkPointPriv::SetRectFan(fan0Pos, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); SkMatrixPriv::MapPointsWithStride(viewMatrix, fan0Pos, vertexStride, 4); // Now create the inset points and then outset the original // rotated points // TL *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1]; // BL *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1]; // BR *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1]; // TR *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1]; } if (localMatrix) { SkMatrix invViewMatrix; if (!viewMatrix.invert(&invViewMatrix)) { SkDebugf("View matrix is non-invertible, local coords will be wrong."); invViewMatrix = SkMatrix::I(); } SkMatrix localCoordMatrix; localCoordMatrix.setConcat(*localMatrix, invViewMatrix); SkPoint* fan0Loc = reinterpret_cast<SkPoint*>(verts + sizeof(SkPoint) + sizeof(GrColor)); SkMatrixPriv::MapPointsWithStride(localCoordMatrix, fan0Loc, vertexStride, fan0Pos, vertexStride, 8); } // Make verts point to vertex color and then set all the color and coverage vertex attrs // values. verts += sizeof(SkPoint); // The coverage offset is always the last vertex attribute intptr_t coverageOffset = vertexStride - sizeof(GrColor) - sizeof(SkPoint); for (int i = 0; i < 4; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + coverageOffset) = 0; } } int scale; if (inset < SK_ScalarHalf) { scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); SkASSERT(scale >= 0 && scale <= 255); } else { scale = 0xff; } verts += 4 * vertexStride; float innerCoverage = GrNormalizeByteToFloat(scale); GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); for (int i = 0; i < 4; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + coverageOffset) = innerCoverage; } } }
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(); mat.reset(); mat.set(SkMatrix::kMSkewX, SK_ScalarNaN); mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN); // fixed pt doesn't have the property that NaN does not equal itself. #ifdef SK_SCALAR_IS_FIXED REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2)); #else REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2)); #endif test_matrix_max_stretch(reporter); }
Boolean Matrix::NativeRectStaysRect( /* [in] */ Int64 objHandle) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); return obj->rectStaysRect() ? TRUE : FALSE; }
void generateAAFillRectGeometry(void* vertices, size_t offset, size_t vertexStride, GrColor color, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect& devRect, bool tweakAlphaForCoverage) const { intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset; SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride); SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1); inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height()); if (viewMatrix.rectStaysRect()) { set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf); set_inset_fan(fan1Pos, vertexStride, devRect, inset, inset); } else { // compute transformed (1, 0) and (0, 1) vectors SkVector vec[2] = { { viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY] }, { viewMatrix[SkMatrix::kMSkewX], viewMatrix[SkMatrix::kMScaleY] } }; vec[0].normalize(); vec[0].scale(SK_ScalarHalf); vec[1].normalize(); vec[1].scale(SK_ScalarHalf); // create the rotated rect fan0Pos->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride); viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4); // Now create the inset points and then outset the original // rotated points // TL *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1]; // BL *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1]; // BR *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1]; // TR *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) = *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1]; *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1]; } // Make verts point to vertex color and then set all the color and coverage vertex attrs // values. verts += sizeof(SkPoint); for (int i = 0; i < 4; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0; } } int scale; if (inset < SK_ScalarHalf) { scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); SkASSERT(scale >= 0 && scale <= 255); } else { scale = 0xff; } verts += 4 * vertexStride; float innerCoverage = GrNormalizeByteToFloat(scale); GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); for (int i = 0; i < 4; ++i) { if (tweakAlphaForCoverage) { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; } else { *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage; } } }