Beispiel #1
0
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;
}
Beispiel #2
0
 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;
 }
Beispiel #3
0
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);
}
Beispiel #4
0
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);
    }
}
Beispiel #5
0
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;
                    }
                }
            }
        }
    }
}
Beispiel #6
0
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;
        }
    }
}
Beispiel #7
0
/*
 * 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();
}
Beispiel #8
0
 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());
 }
Beispiel #9
0
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;

} 
Beispiel #12
0
// 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());
}
Beispiel #13
0
static bool almost_equal(float a, float b) {
    return SkTAbs(a - b) < 0.001f;
}
Beispiel #14
0
static bool color_space_almost_equal(float a, float b) {
    return SkTAbs(a - b) < 0.01f;
}
Beispiel #15
0
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);
}
Beispiel #18
0
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");
    }
}