static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& nativeDims, const SkISize& scaledCodecDims, float desiredScale) { if (nativeDims == scaledCodecDims) { // does not matter which to return if equal. Return here to skip below calculations return nativeDims; } float idealWidth = origDims.width() * desiredScale; float idealHeight = origDims.height() * desiredScale; // calculate difference between native dimensions and ideal dimensions float nativeWDiff = SkTAbs(idealWidth - nativeDims.width()); float nativeHDiff = SkTAbs(idealHeight - nativeDims.height()); float nativeDiff = nativeWDiff + nativeHDiff; // Native scaling is preferred to sampling. If we can scale natively to // within one of the ideal value, we should choose to scale natively. if (nativeWDiff < 1.0f && nativeHDiff < 1.0f) { return nativeDims; } // calculate difference between scaledCodec dimensions and ideal dimensions float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width()); float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height()); float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff; // return dimensions closest to ideal dimensions. // If the differences are equal, return nativeDims, as native scaling is more efficient. return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims; }
static int match_score(const SkFontStyle& pattern, const SkFontStyle& candidate) { int score = 0; score += SkTAbs((pattern.width() - candidate.width()) * 100); score += SkTAbs((pattern.slant() == candidate.slant()) ? 0 : 1000); score += SkTAbs(pattern.weight() - candidate.weight()); return score; }
static SkString pdf_date(const SkTime::DateTime& dt) { int timeZoneMinutes = SkToInt(dt.fTimeZoneMinutes); char timezoneSign = timeZoneMinutes >= 0 ? '+' : '-'; int timeZoneHours = SkTAbs(timeZoneMinutes) / 60; timeZoneMinutes = SkTAbs(timeZoneMinutes) % 60; return SkStringPrintf( "D:%04u%02u%02u%02u%02u%02u%c%02d'%02d'", static_cast<unsigned>(dt.fYear), static_cast<unsigned>(dt.fMonth), static_cast<unsigned>(dt.fDay), static_cast<unsigned>(dt.fHour), static_cast<unsigned>(dt.fMinute), static_cast<unsigned>(dt.fSecond), timezoneSign, timeZoneHours, timeZoneMinutes); }
void SkTime::DateTime::toISO8601(SkString* dst) const { if (dst) { int timeZoneMinutes = SkToInt(fTimeZoneMinutes); char timezoneSign = timeZoneMinutes >= 0 ? '+' : '-'; int timeZoneHours = SkTAbs(timeZoneMinutes) / 60; timeZoneMinutes = SkTAbs(timeZoneMinutes) % 60; dst->printf("%04u-%02u-%02uT%02u:%02u:%02u%c%02d:%02d", static_cast<unsigned>(fYear), static_cast<unsigned>(fMonth), static_cast<unsigned>(fDay), static_cast<unsigned>(fHour), static_cast<unsigned>(fMinute), static_cast<unsigned>(fSecond), timezoneSign, timeZoneHours, timeZoneMinutes); } }
DEF_TEST(PathOpsAngleFindCrossEpsilon, reporter) { if (gDisableAngleTests) { return; } SkRandom ran; int maxEpsilon = 0; for (int index = 0; index < 10000000; ++index) { SkDLine line = {{{0, 0}, {ran.nextRangeF(0.0001f, 1000), ran.nextRangeF(0.0001f, 1000)}}}; for (int inner = 0; inner < 10; ++inner) { float t = ran.nextRangeF(0.0001f, 1); SkDPoint dPt = line.ptAtT(t); SkPoint pt = dPt.asSkPoint(); float xs[3] = { prev(pt.fX), pt.fX, next(pt.fX) }; float ys[3] = { prev(pt.fY), pt.fY, next(pt.fY) }; for (int xIdx = 0; xIdx < 3; ++xIdx) { for (int yIdx = 0; yIdx < 3; ++yIdx) { SkPoint test = { xs[xIdx], ys[yIdx] }; float p1 = SkDoubleToScalar(line[1].fX * test.fY); float p2 = SkDoubleToScalar(line[1].fY * test.fX); int p1Bits = SkFloatAs2sCompliment(p1); int p2Bits = SkFloatAs2sCompliment(p2); int epsilon = SkTAbs(p1Bits - p2Bits); if (maxEpsilon < epsilon) { SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g pt={%1.7g, %1.7g}" " epsilon=%d\n", line[1].fX, line[1].fY, t, test.fX, test.fY, epsilon); maxEpsilon = epsilon; } } } } } }
DEF_TEST(PathOpsAngleFindQuadEpsilon, reporter) { if (gDisableAngleTests) { return; } SkRandom ran; int maxEpsilon = 0; double maxAngle = 0; for (int index = 0; index < 100000; ++index) { SkDLine line = {{{0, 0}, {ran.nextRangeF(0.0001f, 1000), ran.nextRangeF(0.0001f, 1000)}}}; float t = ran.nextRangeF(0.0001f, 1); SkDPoint dPt = line.ptAtT(t); float t2 = ran.nextRangeF(0.0001f, 1); SkDPoint qPt = line.ptAtT(t2); float t3 = ran.nextRangeF(0.0001f, 1); SkDPoint qPt2 = line.ptAtT(t3); qPt.fX += qPt2.fY; qPt.fY -= qPt2.fX; QuadPts q = {{line[0], dPt, qPt}}; SkDQuad quad; quad.debugSet(q.fPts); // binary search for maximum movement of quad[1] towards test that still has 1 intersection double moveT = 0.5f; double deltaT = moveT / 2; SkDPoint last; do { last = quad[1]; quad[1].fX = dPt.fX - line[1].fY * moveT; quad[1].fY = dPt.fY + line[1].fX * moveT; SkIntersections i; i.intersect(quad, line); REPORTER_ASSERT(reporter, i.used() > 0); if (i.used() == 1) { moveT += deltaT; } else { moveT -= deltaT; } deltaT /= 2; } while (last.asSkPoint() != quad[1].asSkPoint()); float p1 = SkDoubleToScalar(line[1].fX * last.fY); float p2 = SkDoubleToScalar(line[1].fY * last.fX); int p1Bits = SkFloatAs2sCompliment(p1); int p2Bits = SkFloatAs2sCompliment(p2); int epsilon = SkTAbs(p1Bits - p2Bits); if (maxEpsilon < epsilon) { SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g" " pt={%1.7g, %1.7g} epsilon=%d\n", line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, epsilon); maxEpsilon = epsilon; } double a1 = atan2(line[1].fY, line[1].fX); double a2 = atan2(last.fY, last.fX); double angle = fabs(a1 - a2); if (maxAngle < angle) { SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g" " pt={%1.7g, %1.7g} angle=%1.7g\n", line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, angle); maxAngle = angle; } } }
/* * Chooses the best dimensions given the desired scale */ SkISize SkIcoCodec::onGetScaledDimensions(float desiredScale) const { // We set the dimensions to the largest candidate image by default. // Regardless of the scale request, this is the largest image that we // will decode. if (desiredScale >= 1.0) { return this->getInfo().dimensions(); } int origWidth = this->getInfo().width(); int origHeight = this->getInfo().height(); float desiredSize = desiredScale * origWidth * origHeight; // At least one image will have smaller error than this initial value float minError = ((float) (origWidth * origHeight)) - desiredSize + 1.0f; int32_t minIndex = -1; for (int32_t i = 0; i < fEmbeddedCodecs->count(); i++) { int width = fEmbeddedCodecs->operator[](i)->getInfo().width(); int height = fEmbeddedCodecs->operator[](i)->getInfo().height(); float error = SkTAbs(((float) (width * height)) - desiredSize); if (error < minError) { minError = error; minIndex = i; } } SkASSERT(minIndex >= 0); return fEmbeddedCodecs->operator[](minIndex)->getInfo().dimensions(); }
void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& p) override { INHERITED::onSetData(pdman, p); const TwoPointConicalEffect& effect = p.cast<TwoPointConicalEffect>(); // kRadialType should imply |r1 - r0| = 1 (after our transformation) SkASSERT(effect.getType() == Type::kStrip || SkScalarNearlyZero(SkTAbs(effect.diffRadius()) - 1)); pdman.set1f(fParamUni, effect.getType() == Type::kRadial ? effect.r0() : effect.r0() * effect.r0()); }
static inline bool almost_equals(SkPMColor a, SkPMColor b, int tolerance) { if (SkTAbs((int)SkGetPackedR32(a) - (int)SkGetPackedR32(b)) > tolerance) { return false; } if (SkTAbs((int)SkGetPackedG32(a) - (int)SkGetPackedG32(b)) > tolerance) { return false; } if (SkTAbs((int)SkGetPackedB32(a) - (int)SkGetPackedB32(b)) > tolerance) { return false; } if (SkTAbs((int)SkGetPackedA32(a) - (int)SkGetPackedA32(b)) > tolerance) { return false; } return true; }
static bool checkEndPoint(double x, double y, const SkDLine& l, double* tPtr, int useX) { if (!between(l[0].fX, x, l[1].fX) || !between(l[0].fY, y, l[1].fY)) { return false; } double xLen = l[1].fX - l[0].fX; double yLen = l[1].fY - l[0].fY; if (useX < 0) { useX = SkTAbs(xLen) > SkTAbs(yLen); } // OPTIMIZATION: do between test before divide double t = useX ? (x - l[0].fX) / xLen : (y - l[0].fY) / yLen; if (!between(0, t, 1)) { return false; } double opp = useX ? (1 - t) * l[0].fY + t * l[1].fY : (1 - t) * l[0].fX + t * l[1].fX; if (!AlmostEqualUlps(opp, useX ? y : x)) { return false; } *tPtr = t; return true; }
static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& nativeDims, const SkISize& scaledCodecDims, float desiredScale) { if (nativeDims == scaledCodecDims) { // does not matter which to return if equal. Return here to skip below calculations return nativeDims; } float idealWidth = origDims.width() * desiredScale; float idealHeight = origDims.height() * desiredScale; // calculate difference between native dimensions and ideal dimensions float nativeWDiff = SkTAbs(idealWidth - nativeDims.width()); float nativeHDiff = SkTAbs(idealHeight - nativeDims.height()); float nativeDiff = (nativeWDiff + nativeHDiff) / 2; // calculate difference between scaledCodec dimensions and ideal dimensions float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width()); float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height()); float scaledCodecDiff = (scaledCodecWDiff + scaledCodecHDiff) / 2; // return dimensions closest to ideal dimensions. // If the differences are equal, return nativeDims, as native scaling is more efficient. return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims; }
// Sanity checks for the GetDateTime function. DEF_TEST(Time_GetDateTime, r) { SkTime::DateTime dateTime; SkTime::GetDateTime(&dateTime); // TODO(future generation): update these values. const uint16_t kMinimumSaneYear = 1964; const uint16_t kMaximumSaneYear = 2064; if (dateTime.fYear < kMinimumSaneYear) { ERRORF(r, "SkTime::GetDateTime: %u (CurrentYear) < %u (MinimumSaneYear)", static_cast<unsigned>(dateTime.fYear), static_cast<unsigned>(kMinimumSaneYear)); } if (dateTime.fYear > kMaximumSaneYear) { ERRORF(r, "SkTime::GetDateTime: %u (CurrentYear) > %u (MaximumSaneYear)", static_cast<unsigned>(dateTime.fYear), static_cast<unsigned>(kMaximumSaneYear)); } REPORTER_ASSERT(r, dateTime.fMonth >= 1); REPORTER_ASSERT(r, dateTime.fMonth <= 12); REPORTER_ASSERT(r, dateTime.fDay >= 1); REPORTER_ASSERT(r, dateTime.fDay <= 31); REPORTER_ASSERT(r, dateTime.fHour <= 23); REPORTER_ASSERT(r, dateTime.fMinute <= 59); REPORTER_ASSERT(r, dateTime.fSecond <= 60); // leap seconds are 23:59:60 // The westernmost timezone is -12:00. // The easternmost timezone is +14:00. REPORTER_ASSERT(r, SkTAbs(SkToInt(dateTime.fTimeZoneMinutes)) <= 14 * 60); SkString timeStamp; dateTime.toISO8601(&timeStamp); REPORTER_ASSERT(r, timeStamp.size() > 0); INFOF(r, "\nCurrent Time (ISO-8601 format): \"%s\"\n", timeStamp.c_str()); }
static bool almost_equal(float a, float b) { return SkTAbs(a - b) < 0.001f; }
static bool color_space_almost_equal(float a, float b) { return SkTAbs(a - b) < 0.01f; }
static int MaxByteDiff(uint32_t v1, uint32_t v2) { return SkMax32(SkMax32(SkTAbs(getByte(v1, 0) - getByte(v2, 0)), SkTAbs(getByte(v1, 1) - getByte(v2, 1))), SkMax32(SkTAbs(getByte(v1, 2) - getByte(v2, 2)), SkTAbs(getByte(v1, 3) - getByte(v2, 3)))); }
bool SkDifferentPixelsMetric::diff(SkBitmap* baseline, SkBitmap* test, const BitmapsToCreate& bitmapsToCreate, Result* result) const { double startTime = get_seconds(); // Ensure the images are comparable if (baseline->width() != test->width() || baseline->height() != test->height() || baseline->width() <= 0 || baseline->height() <= 0 || baseline->colorType() != test->colorType()) { SkASSERT(baseline->width() == test->width()); SkASSERT(baseline->height() == test->height()); SkASSERT(baseline->width() > 0); SkASSERT(baseline->height() > 0); SkASSERT(baseline->colorType() == test->colorType()); return false; } int width = baseline->width(); int height = baseline->height(); int maxRedDiff = 0; int maxGreenDiff = 0; int maxBlueDiff = 0; // Prepare any bitmaps we will be filling in if (bitmapsToCreate.alphaMask) { result->poiAlphaMask.allocPixels(SkImageInfo::MakeA8(width, height)); result->poiAlphaMask.eraseARGB(SK_AlphaOPAQUE, 0, 0, 0); } if (bitmapsToCreate.rgbDiff) { result->rgbDiffBitmap.allocPixels(SkImageInfo::Make(width, height, baseline->colorType(), kPremul_SkAlphaType)); result->rgbDiffBitmap.eraseARGB(SK_AlphaTRANSPARENT, 0, 0, 0); } if (bitmapsToCreate.whiteDiff) { result->whiteDiffBitmap.allocPixels(SkImageInfo::MakeN32Premul(width, height)); result->whiteDiffBitmap.eraseARGB(SK_AlphaOPAQUE, 0, 0, 0); } // Prepare the pixels for comparison result->poiCount = 0; baseline->lockPixels(); test->lockPixels(); for (int y = 0; y < height; y++) { // Grab a row from each image for easy comparison // TODO(epoger): The code below already assumes 4 bytes per pixel, so I think // we could just call getAddr32() to save a little time. // OR, if we want to play it safe, call ComputeBytesPerPixel instead // of assuming 4 bytes per pixel. uint32_t* baselineRow = static_cast<uint32_t *>(baseline->getAddr(0, y)); uint32_t* testRow = static_cast<uint32_t *>(test->getAddr(0, y)); for (int x = 0; x < width; x++) { // Compare one pixel at a time so each differing pixel can be noted // TODO(epoger): This loop looks like a good place to work on performance, // but we should run the code through a profiler to be sure. uint32_t baselinePixel = baselineRow[x]; uint32_t testPixel = testRow[x]; if (baselinePixel != testPixel) { result->poiCount++; int redDiff = SkTAbs(static_cast<int>(SkColorGetR(baselinePixel) - SkColorGetR(testPixel))); if (redDiff > maxRedDiff) {maxRedDiff = redDiff;} int greenDiff = SkTAbs(static_cast<int>(SkColorGetG(baselinePixel) - SkColorGetG(testPixel))); if (greenDiff > maxGreenDiff) {maxGreenDiff = greenDiff;} int blueDiff = SkTAbs(static_cast<int>(SkColorGetB(baselinePixel) - SkColorGetB(testPixel))); if (blueDiff > maxBlueDiff) {maxBlueDiff = blueDiff;} if (bitmapsToCreate.alphaMask) { *result->poiAlphaMask.getAddr8(x,y) = SK_AlphaTRANSPARENT; } if (bitmapsToCreate.rgbDiff) { *result->rgbDiffBitmap.getAddr32(x,y) = SkColorSetRGB(redDiff, greenDiff, blueDiff); } if (bitmapsToCreate.whiteDiff) { *result->whiteDiffBitmap.getAddr32(x,y) = SK_ColorWHITE; } } } } test->unlockPixels(); baseline->unlockPixels(); result->maxRedDiff = maxRedDiff; result->maxGreenDiff = maxGreenDiff; result->maxBlueDiff = maxBlueDiff; if (bitmapsToCreate.alphaMask) { result->poiAlphaMask.unlockPixels(); } if (bitmapsToCreate.rgbDiff) { result->rgbDiffBitmap.unlockPixels(); } if (bitmapsToCreate.whiteDiff) { result->whiteDiffBitmap.unlockPixels(); } // Calculates the percentage of identical pixels result->result = 1.0 - ((double)result->poiCount / (width * height)); result->timeElapsed = get_seconds() - startTime; return true; }
/** * Calculates the area of the triangular gamut. */ float calculate_area(SkPoint abc[]) { SkPoint a = abc[0]; SkPoint b = abc[1]; SkPoint c = abc[2]; return 0.5f * SkTAbs(a.fX*b.fY + b.fX*c.fY - a.fX*c.fY - c.fX*b.fY - b.fX*a.fY); }
static bool almost_equal(int x, int y) { return SkTAbs(x - y) <= 1; }
/** * Copies a FT_Bitmap into an SkMask with the same dimensions. * * Yes, No, Never Requested, Never Produced * * kBW kA8 k3D kARGB32 kLCD16 * FT_PIXEL_MODE_MONO Y Y NR N Y * FT_PIXEL_MODE_GRAY N Y NR N Y * FT_PIXEL_MODE_GRAY2 NP NP NR NP NP * FT_PIXEL_MODE_GRAY4 NP NP NR NP NP * FT_PIXEL_MODE_LCD NP NP NR NP NP * FT_PIXEL_MODE_LCD_V NP NP NR NP NP * FT_PIXEL_MODE_BGRA N N NR Y N * * TODO: All of these N need to be Y or otherwise ruled out. */ static void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) { SkASSERTF(dstMask.fBounds.width() == static_cast<int>(srcFTBitmap.width), "dstMask.fBounds.width() = %d\n" "static_cast<int>(srcFTBitmap.width) = %d", dstMask.fBounds.width(), static_cast<int>(srcFTBitmap.width) ); SkASSERTF(dstMask.fBounds.height() == static_cast<int>(srcFTBitmap.rows), "dstMask.fBounds.height() = %d\n" "static_cast<int>(srcFTBitmap.rows) = %d", dstMask.fBounds.height(), static_cast<int>(srcFTBitmap.rows) ); const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer); const FT_Pixel_Mode srcFormat = static_cast<FT_Pixel_Mode>(srcFTBitmap.pixel_mode); // FT_Bitmap::pitch is an int and allowed to be negative. const int srcPitch = srcFTBitmap.pitch; const size_t srcRowBytes = SkTAbs(srcPitch); uint8_t* dst = dstMask.fImage; const SkMask::Format dstFormat = static_cast<SkMask::Format>(dstMask.fFormat); const size_t dstRowBytes = dstMask.fRowBytes; const size_t width = srcFTBitmap.width; const size_t height = srcFTBitmap.rows; if (SkMask::kLCD16_Format == dstFormat) { copyFT2LCD16<false>(srcFTBitmap, dstMask, false, nullptr, nullptr, nullptr); return; } if ((FT_PIXEL_MODE_MONO == srcFormat && SkMask::kBW_Format == dstFormat) || (FT_PIXEL_MODE_GRAY == srcFormat && SkMask::kA8_Format == dstFormat)) { size_t commonRowBytes = SkTMin(srcRowBytes, dstRowBytes); for (size_t y = height; y --> 0;) { memcpy(dst, src, commonRowBytes); src += srcPitch; dst += dstRowBytes; } } else if (FT_PIXEL_MODE_MONO == srcFormat && SkMask::kA8_Format == dstFormat) { for (size_t y = height; y --> 0;) { uint8_t byte = 0; int bits = 0; const uint8_t* src_row = src; uint8_t* dst_row = dst; for (size_t x = width; x --> 0;) { if (0 == bits) { byte = *src_row++; bits = 8; } *dst_row++ = byte & 0x80 ? 0xff : 0x00; bits--; byte <<= 1; } src += srcPitch; dst += dstRowBytes; } } else if (FT_PIXEL_MODE_BGRA == srcFormat && SkMask::kARGB32_Format == dstFormat) { // FT_PIXEL_MODE_BGRA is pre-multiplied. for (size_t y = height; y --> 0;) { const uint8_t* src_row = src; SkPMColor* dst_row = reinterpret_cast<SkPMColor*>(dst); for (size_t x = 0; x < width; ++x) { uint8_t b = *src_row++; uint8_t g = *src_row++; uint8_t r = *src_row++; uint8_t a = *src_row++; *dst_row++ = SkPackARGB32(a, r, g, b); #ifdef SK_SHOW_TEXT_BLIT_COVERAGE *(dst_row-1) = SkFourByteInterp256(*(dst_row-1), SK_ColorWHITE, 0x40); #endif } src += srcPitch; dst += dstRowBytes; } } else { SkDEBUGF(("FT_Pixel_Mode %d, SkMask::Format %d\n", srcFormat, dstFormat)); SkDEBUGFAIL("unsupported combination of FT_Pixel_Mode and SkMask::Format"); } }