Exemple #1
0
SkString GrAtlasTextBatch::dumpInfo() const {
    SkString str;

    for (int i = 0; i < fGeoCount; ++i) {
        str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
                    i,
                    fGeoData[i].fColor,
                    fGeoData[i].fX,
                    fGeoData[i].fY,
                    fGeoData[i].fBlob->runCount());
    }

    str.append(INHERITED::dumpInfo());
    return str;
}
Exemple #2
0
static void saveFile(const char name[], const char config[], const char dir[],
                     const SkImage* image) {
    SkAutoTUnref<SkData> data(image->encode(SkImageEncoder::kPNG_Type, 100));
    if (NULL == data.get()) {
        return;
    }

    SkString filename;
    make_filename(name, &filename);
    filename.appendf("_%s.png", config);
    SkString path = SkOSPath::SkPathJoin(dir, filename.c_str());
    ::remove(path.c_str());

    SkFILEWStream   stream(path.c_str());
    stream.write(data->data(), data->size());
}
Exemple #3
0
    virtual const char* onGetName() {
        fName.set("bitmap");
        fName.appendf("_%s%s", sk_tool_utils::colortype_name(fColorType),
                      kOpaque_SkAlphaType == fAlphaType ? "" : "_A");
        if (fDoScale) {
            fName.append("_scale");
        }
        if (fForceUpdate) {
            fName.append("_update");
        }
        if (fIsVolatile) {
            fName.append("_volatile");
        }

        return fName.c_str();
    }
Exemple #4
0
static void push_brd_src(Path path, SkBitmapRegionDecoder::Strategy strategy,
        CodecSrc::DstColorType dstColorType, BRDSrc::Mode mode, uint32_t sampleSize) {
    SkString folder;
    switch (strategy) {
        case SkBitmapRegionDecoder::kCanvas_Strategy:
            folder.append("brd_canvas");
            break;
        case SkBitmapRegionDecoder::kAndroidCodec_Strategy:
            folder.append("brd_android_codec");
            break;
        default:
            SkASSERT(false);
            return;
    }

    switch (mode) {
        case BRDSrc::kFullImage_Mode:
            break;
        case BRDSrc::kDivisor_Mode:
            folder.append("_divisor");
            break;
        default:
            SkASSERT(false);
            return;
    }

    switch (dstColorType) {
        case CodecSrc::kGetFromCanvas_DstColorType:
            break;
        case CodecSrc::kIndex8_Always_DstColorType:
            folder.append("_kIndex");
            break;
        case CodecSrc::kGrayscale_Always_DstColorType:
            folder.append("_kGray");
            break;
        default:
            SkASSERT(false);
            return;
    }

    if (1 != sampleSize) {
        folder.appendf("_%.3f", 1.0f / (float) sampleSize);
    }

    BRDSrc* src = new BRDSrc(path, strategy, mode, dstColorType, sampleSize);
    push_src("image", folder, src);
}
Exemple #5
0
    SkString dumpInfo() const override {
        SkString str;

        for (int i = 0; i < fGeoData.count(); ++i) {
            str.appendf("%d: Color: 0x%08x Center [L: %d, T: %d, R: %d, B: %d], "
                        "Dst [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
                        i,
                        fGeoData[i].fColor,
                        fGeoData[i].fCenter.fLeft, fGeoData[i].fCenter.fTop,
                        fGeoData[i].fCenter.fRight, fGeoData[i].fCenter.fBottom,
                        fGeoData[i].fDst.fLeft, fGeoData[i].fDst.fTop,
                        fGeoData[i].fDst.fRight, fGeoData[i].fDst.fBottom);
        }

        str.append(INHERITED::dumpInfo());
        return str;
    }
Exemple #6
0
    void iterateFamily(SkCanvas* canvas, const SkPaint& paint,
                       SkFontStyleSet* fset) {
        SkPaint p(paint);
        SkScalar y = 0;

        for (int j = 0; j < fset->count(); ++j) {
            SkString sname;
            SkFontStyle fs;
            fset->getStyle(j, &fs, &sname);

            sname.appendf(" [%d %d]", fs.weight(), fs.width());

            SkSafeUnref(p.setTypeface(fset->createTypeface(j)));
            (void)drawString(canvas, sname, 0, y, p);
            y += 24;
        }
    }
Exemple #7
0
 static void forever(void*) {
     for (;;) {
         static const int kSec = 300;
     #if defined(SK_BUILD_FOR_WIN)
         Sleep(kSec * 1000);
     #else
         sleep(kSec);
     #endif
         SkString running;
         {
             SkAutoMutexAcquire lock(gRunningMutex);
             for (int i = 0; i < gRunning.count(); i++) {
                 running.appendf("\n\t%s", gRunning[i].c_str());
             }
         }
         SkDebugf("\nCurrently running:%s\n", running.c_str());
     }
 }
Exemple #8
0
static void push_android_codec_src(Path path, AndroidCodecSrc::Mode mode,
        CodecSrc::DstColorType dstColorType, SkAlphaType dstAlphaType, int sampleSize) {
    SkString folder;
    switch (mode) {
        case AndroidCodecSrc::kFullImage_Mode:
            folder.append("scaled_codec");
            break;
        case AndroidCodecSrc::kDivisor_Mode:
            folder.append("scaled_codec_divisor");
            break;
    }

    switch (dstColorType) {
        case CodecSrc::kGrayscale_Always_DstColorType:
            folder.append("_kGray8");
            break;
        case CodecSrc::kIndex8_Always_DstColorType:
            folder.append("_kIndex8");
            break;
        default:
            break;
    }

    switch (dstAlphaType) {
        case kOpaque_SkAlphaType:
            folder.append("_opaque");
            break;
        case kPremul_SkAlphaType:
            folder.append("_premul");
            break;
        case kUnpremul_SkAlphaType:
            folder.append("_unpremul");
            break;
        default:
            break;
    }

    if (1 != sampleSize) {
        folder.appendf("_%.3f", 1.0f / (float) sampleSize);
    }

    AndroidCodecSrc* src = new AndroidCodecSrc(path, mode, dstColorType, dstAlphaType, sampleSize);
    push_src("image", folder, src);
}
Exemple #9
0
static void generate_index(const char* defaultName, FILE* out) {
    int fontCount = gWritten.count();
    fprintf(out,
            "static SkTestFontData gTestFonts[] = {\n");
    int fontIndex;
    for (fontIndex = 0; fontIndex < fontCount; ++fontIndex) {
        const FontWritten& writ = gWritten[fontIndex];
        const char* name = writ.fName;
        SkString strippedStr = strip_spaces(SkString(name));
        strippedStr.appendf("%s", gStyleName[writ.fStyle]);
        const char* strip = strippedStr.c_str();
        fprintf(out,
                "    {    %sPoints, %sVerbs, %sCharCodes,\n"
                "         %sCharCodesCount, %sWidths,\n"
                "         %sMetrics, \"%s\", SkTypeface::%s, NULL\n"
                "    },\n",
                strip, strip, strip, strip, strip, strip, name, gStyleName[writ.fStyle]);
    }
    fprintf(out, "};\n\n");
    fprintf(out, "const int gTestFontsCount = (int) SK_ARRAY_COUNT(gTestFonts);\n\n");
    fprintf(out,
                "struct SubFont {\n"
                "    const char* fName;\n"
                "    SkTypeface::Style fStyle;\n"
                "    SkTestFontData& fFont;\n"
                "    const char* fFile;\n"
                "};\n\n"
                "const SubFont gSubFonts[] = {\n");
    int defaultIndex = -1;
    for (int subIndex = 0; subIndex < gFontsCount; subIndex++) {
        const FontDesc& desc = gFonts[subIndex];
        if (!strcmp(defaultName, desc.fName)) {
            defaultIndex = subIndex;
        }
        fprintf(out,
                "    { \"%s\", SkTypeface::%s, gTestFonts[%d], \"%s\"},\n", desc.fName,
                gStyleName[desc.fStyle], desc.fFontIndex, desc.fFile);
    }
    fprintf(out, "};\n\n");
    fprintf(out, "const int gSubFontsCount = (int) SK_ARRAY_COUNT(gSubFonts);\n\n");
    SkASSERT(defaultIndex >= 0);
    fprintf(out, "const int gDefaultFontIndex = %d;\n", defaultIndex);
}
Exemple #10
0
static SkString generate_template(SkString source) {
    SkString debuggerTemplate;
    debuggerTemplate.appendf(
        "<!DOCTYPE html>\n"
        "<html>\n"
        "<head>\n"
        "    <title>SkDebugger</title>\n"
        "    <meta charset=\"utf-8\" />\n"
        "    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
        "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
        "    <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
        "    <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
        "    <link rel='shortcut icon' href='https://debugger.skia.org/res/img/favicon.ico' type='image/x-icon'/ >"
        "</head>\n"
        "<body class=\"fullbleed layout vertical\">\n"
            "  <debugger-app-sk>This is the app."
            "  </debugger-app-sk>\n"
        "</body>\n"
        "</html>", source.c_str(), source.c_str());
    return debuggerTemplate;
}
Exemple #11
0
static SkString generate_template(SkString source) {
    SkString debuggerTemplate;
    debuggerTemplate.appendf(
        "<!DOCTYPE html>\n"
        "<html>\n"
        "<head>\n"
        "    <title>SkDebugger</title>\n"
        "    <meta charset='utf-8' />\n"
        "    <meta http-equiv='X-UA-Compatible' content='IE=egde,chrome=1'>\n"
        "    <meta name='viewport' content='width=device-width, initial-scale=1.0'>\n"
        "    <script src='%s/res/js/core.js' type='text/javascript' charset='utf-8'></script>\n"
        "    <link href='%s/res/vul/elements.html' rel='import' />\n"
        "    <link rel='shortcut icon' href='%s/res/img/favicon.ico' type='image/x-icon'/ >"
        "</head>\n"
        "<body class='fullbleed layout vertical'>\n"
            "  <debugger-app-sk>This is the app."
            "  </debugger-app-sk>\n"
        "</body>\n"
        "</html>", source.c_str(), source.c_str(), source.c_str());
    return debuggerTemplate;
}
DEF_TEST(SkRecordingAccuracyXfermode, reporter) {
#define FINEGRAIN 0
    const Drawer drawer;

    BitmapBackedCanvasStrategy golden(drawer.imageInfo());
    PictureStrategy picture(drawer.imageInfo());

#if !FINEGRAIN
    unsigned numErrors = 0;
    SkString errors;
#endif

    for (int iMode = 0; iMode < int(SkBlendMode::kLastMode); iMode++) {
        const SkRect& clip = SkRect::MakeXYWH(100, 0, 100, 100);
        SkBlendMode mode = SkBlendMode(iMode);

        const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode);
        const SkBitmap& pictureBM = picture.recordAndReplay(drawer, clip, mode);

        size_t pixelsSize = goldenBM.computeByteSize();
        REPORTER_ASSERT(reporter, pixelsSize == pictureBM.computeByteSize());

        // The pixel arrays should match.
#if FINEGRAIN
        REPORTER_ASSERT(reporter,
                        0 == memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize));
#else
        if (memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)) {
            numErrors++;
            errors.appendf("For SkXfermode %d %s:    SkPictureRecorder bitmap is wrong\n",
                           iMode, SkBlendMode_Name(mode));
        }
#endif
    }

#if !FINEGRAIN
    REPORTER_ASSERT(reporter, 0 == numErrors, errors.c_str());
#endif
}
Exemple #13
0
static void push_codec_src(Path path, CodecSrc::Mode mode, CodecSrc::DstColorType dstColorType,
        float scale) {
    SkString folder;
    switch (mode) {
        case CodecSrc::kCodec_Mode:
            folder.append("codec");
            break;
        case CodecSrc::kScanline_Mode:
            folder.append("scanline");
            break;
        case CodecSrc::kScanline_Subset_Mode:
            folder.append("scanline_subset");
            break;
        case CodecSrc::kStripe_Mode:
            folder.append("stripe");
            break;
        case CodecSrc::kSubset_Mode:
            folder.append("codec_subset");
            break;
    }

    switch (dstColorType) {
        case CodecSrc::kGrayscale_Always_DstColorType:
            folder.append("_kGray8");
            break;
        case CodecSrc::kIndex8_Always_DstColorType:
            folder.append("_kIndex8");
            break;
        default:
            break;
    }

    if (1.0f != scale) {
        folder.appendf("_%.3f", scale);
    }

    CodecSrc* src = new CodecSrc(path, mode, dstColorType, scale);
    push_src("image", folder, src);
}
Exemple #14
0
    void onDraw(SkCanvas* canvas) override {
        SkScalar y = 20;
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setLCDRenderText(true);
        paint.setSubpixelText(true);
        paint.setTextSize(17);

        SkFontMgr* fm = fFM;
        int count = SkMin32(fm->countFamilies(), MAX_FAMILIES);

        for (int i = 0; i < count; ++i) {
            SkString familyName;
            fm->getFamilyName(i, &familyName);
            paint.setTypeface(nullptr);
            (void)drawString(canvas, familyName, 20, y, paint);

            SkScalar x = 220;

            SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i));
            for (int j = 0; j < set->count(); ++j) {
                SkString sname;
                SkFontStyle fs;
                set->getStyle(j, &fs, &sname);
                sname.appendf(" [%d %d %d]", fs.weight(), fs.width(), fs.slant());

                SkSafeUnref(paint.setTypeface(set->createTypeface(j)));
                x = drawString(canvas, sname, x, y, paint) + 20;

                // check to see that we get different glyphs in japanese and chinese
                x = drawCharacter(canvas, 0x5203, x, y, paint, fm, familyName.c_str(), &zh, 1, fs);
                x = drawCharacter(canvas, 0x5203, x, y, paint, fm, familyName.c_str(), &ja, 1, fs);
                // check that emoji characters are found
                x = drawCharacter(canvas, 0x1f601, x, y, paint, fm, familyName.c_str(), nullptr,0, fs);
            }
            y += 24;
        }
    }
Exemple #15
0
    void sniff(const void* ptr, size_t len) {
        SkMD5 md5;
        md5.write(ptr, len);
        SkMD5::Digest digest;
        md5.finish(digest);

        if (gSeen.contains(digest)) {
            return;
        }
        gSeen.add(digest);

        SkAutoTUnref<SkData> data(SkData::NewWithoutCopy(ptr, len));
        SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(data));
        if (!codec) {
            gUnknown++;
            return;
        }
        SkString ext;
        switch (codec->getEncodedFormat()) {
            case SkEncodedFormat::kBMP_SkEncodedFormat:  ext =  "bmp"; break;
            case SkEncodedFormat::kGIF_SkEncodedFormat:  ext =  "gif"; break;
            case SkEncodedFormat::kICO_SkEncodedFormat:  ext =  "ico"; break;
            case SkEncodedFormat::kJPEG_SkEncodedFormat: ext =  "jpg"; break;
            case SkEncodedFormat::kPNG_SkEncodedFormat:  ext =  "png"; break;
            case SkEncodedFormat::kRAW_SkEncodedFormat:  ext =  "dng"; break;
            case SkEncodedFormat::kWBMP_SkEncodedFormat: ext = "wbmp"; break;
            case SkEncodedFormat::kWEBP_SkEncodedFormat: ext = "webp"; break;
            default: gUnknown++; return;
        }

        SkString path;
        path.appendf("%s/%d.%s", gOutputDir, gKnown++, ext.c_str());

        SkFILEWStream file(path.c_str());
        file.write(ptr, len);

        SkDebugf("%s\n", path.c_str());
    }
Exemple #16
0
static void push_android_codec_src(Path path, CodecSrc::DstColorType dstColorType,
        SkAlphaType dstAlphaType, int sampleSize) {
    SkString folder;
    folder.append("scaled_codec");

    switch (dstColorType) {
        case CodecSrc::kGrayscale_Always_DstColorType:
            folder.append("_kGray8");
            break;
        case CodecSrc::kIndex8_Always_DstColorType:
            folder.append("_kIndex8");
            break;
        case CodecSrc::kNonNative8888_Always_DstColorType:
            folder.append("_kNonNative");
            break;
        default:
            break;
    }

    switch (dstAlphaType) {
        case kPremul_SkAlphaType:
            folder.append("_premul");
            break;
        case kUnpremul_SkAlphaType:
            folder.append("_unpremul");
            break;
        default:
            break;
    }

    if (1 != sampleSize) {
        folder.appendf("_%.3f", 1.0f / (float) sampleSize);
    }

    AndroidCodecSrc* src = new AndroidCodecSrc(path, dstColorType, dstAlphaType, sampleSize);
    push_src("image", folder, src);
}
int tool_main(int argc, char** argv) {
    SkAutoGraphics ag;
    SkString usage;
    usage.printf("Usage: filename [filename]*\n");

    if (argc < 2) {
        SkDebugf("%s\n", usage.c_str());
        return -1;
    }

    SkTArray<Histogram> histograms[kNumBenchmarks];

    for (size_t i = 0; i < kNumBenchmarks; ++i) {
        histograms[i].reset(argc - 1);
        bool success = benchmark_loop(
                argc, argv,
                BenchmarkControl::Make(i),
                histograms[i]);
        if (!success) {
            SkDebugf("benchmark_loop failed at index %d", i);
        }
    }

    // Output gnuplot readable histogram data..
    const char* pbTitle = "bbh_shootout_playback.dat";
    const char* recTitle = "bbh_shootout_record.dat";
    SkFILEWStream playbackOut(pbTitle);
    SkFILEWStream recordOut(recTitle);
    recordOut.writeText("# ");
    playbackOut.writeText("# ");
    for (size_t i = 0; i < kNumBenchmarks; ++i) {
        SkString out;
        out.printf("%s ", BenchmarkControl::getBenchmarkName(i).c_str());
        if (BenchmarkControl::getBenchmarkFunc(i) == &benchmark_recording) {
            recordOut.writeText(out.c_str());
        }
        if (BenchmarkControl::getBenchmarkFunc(i) == &benchmark_playback) {
            playbackOut.writeText(out.c_str());
        }
    }
    recordOut.writeText("\n");
    playbackOut.writeText("\n");

    for (int i = 0; i < argc - 1; ++i) {
        SkString pbLine;
        SkString recLine;
        // ==== Write record info
        recLine.printf("%d ", i);
        recLine.appendf("%f ", histograms[0][i].fCpuTime);  // Append normal_record time
        recLine.appendf("%f", histograms[1][i].fCpuTime);  // Append rtree_record time

        // ==== Write playback info
        pbLine.printf("%d ", i);
        pbLine.appendf("%f ", histograms[2][i].fCpuTime);  // Start with normal playback time.
        // Append all playback benchmark times.
        for (size_t j = kNumBbhPlaybackBenchmarks; j < kNumBenchmarks; ++j) {
            pbLine.appendf("%f ", histograms[j][i].fCpuTime);
        }
        pbLine.remove(pbLine.size() - 1, 1);  // Remove trailing space from line.
        pbLine.appendf("\n");
        recLine.appendf("\n");
        playbackOut.writeText(pbLine.c_str());
        recordOut.writeText(recLine.c_str());
    }
    SkDebugf("\nWrote data to gnuplot-readable files: %s %s\n", pbTitle, recTitle);

    return 0;
}
static void testOpCubicsMain(PathOpsThreadState* data) {
#if DEBUG_SHOW_TEST_NAME
    strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
#endif
    SkASSERT(data);
    PathOpsThreadState& state = *data;
    SkString pathStr;
    for (int a = 0 ; a < 6; ++a) {
        for (int b = a + 1 ; b < 7; ++b) {
            for (int c = 0 ; c < 6; ++c) {
                for (int d = c + 1 ; d < 7; ++d) {
                    for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
    for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) {
        SkPath pathA, pathB;
        pathA.setFillType((SkPath::FillType) e);
        pathA.moveTo(SkIntToScalar(state.fA), SkIntToScalar(state.fB));
        pathA.cubicTo(SkIntToScalar(state.fC), SkIntToScalar(state.fD), SkIntToScalar(b),
                SkIntToScalar(a), SkIntToScalar(d), SkIntToScalar(c));
        pathA.close();
        pathB.setFillType((SkPath::FillType) f);
        pathB.moveTo(SkIntToScalar(a), SkIntToScalar(b));
        pathB.cubicTo(SkIntToScalar(c), SkIntToScalar(d), SkIntToScalar(state.fB),
                SkIntToScalar(state.fA), SkIntToScalar(state.fD), SkIntToScalar(state.fC));
        pathB.close();
        for (int op = 0 ; op <= kXOR_SkPathOp; ++op)    {
            if (state.fReporter->verbose()) {
                pathStr.printf("static void cubicOp%d(skiatest::Reporter* reporter,"
                        " const char* filename) {\n", loopNo);
                pathStr.appendf("    SkPath path, pathB;\n");
                pathStr.appendf("    path.setFillType(SkPath::k%s_FillType);\n",
                        e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
                        ? "EvenOdd" : "?UNDEFINED");
                pathStr.appendf("    path.moveTo(%d,%d);\n", state.fA, state.fB);
                pathStr.appendf("    path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.fC, state.fD,
                        b, a, d, c);
                pathStr.appendf("    path.close();\n");
                pathStr.appendf("    pathB.setFillType(SkPath::k%s_FillType);\n",
                        f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
                        ? "EvenOdd" : "?UNDEFINED");
                pathStr.appendf("    pathB.moveTo(%d,%d);\n", a, b);
                pathStr.appendf("    pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
                        state.fB, state.fA, state.fD, state.fC);
                pathStr.appendf("    pathB.close();\n");
                pathStr.appendf("    testPathOp(reporter, path, pathB, %s, filename);\n",
                        SkPathOpsDebug::OpStr((SkPathOp) op));
                pathStr.appendf("}\n");
                outputProgress(state.fPathStr, pathStr.c_str(), (SkPathOp) op);
            }
            if (!testPathOp(state.fReporter, pathA, pathB, (SkPathOp) op, "cubics")) {
                if (state.fReporter->verbose()) {
                    ++loopNo;
                    goto skipToNext;
                }
            }
        }
    }
                    }
skipToNext: ;
                }
            }
        }
    }
}
Exemple #19
0
SkString GrDrawTargetCaps::dump() const {
    SkString r;
    static const char* gNY[] = {"NO", "YES"};
    r.appendf("MIP Map Support                    : %s\n", gNY[fMipMapSupport]);
    r.appendf("NPOT Texture Tile Support          : %s\n", gNY[fNPOTTextureTileSupport]);
    r.appendf("Two Sided Stencil Support          : %s\n", gNY[fTwoSidedStencilSupport]);
    r.appendf("Stencil Wrap Ops  Support          : %s\n", gNY[fStencilWrapOpsSupport]);
    r.appendf("HW AA Lines Support                : %s\n", gNY[fHWAALineSupport]);
    r.appendf("Shader Derivative Support          : %s\n", gNY[fShaderDerivativeSupport]);
    r.appendf("Geometry Shader Support            : %s\n", gNY[fGeometryShaderSupport]);
    r.appendf("Dual Source Blending Support       : %s\n", gNY[fDualSourceBlendingSupport]);
    r.appendf("Path Rendering Support             : %s\n", gNY[fPathRenderingSupport]);
    r.appendf("Dst Read In Shader Support         : %s\n", gNY[fDstReadInShaderSupport]);
    r.appendf("Discard Render Target Support      : %s\n", gNY[fDiscardRenderTargetSupport]);
    r.appendf("Reuse Scratch Textures             : %s\n", gNY[fReuseScratchTextures]);
    r.appendf("Gpu Tracing Support                : %s\n", gNY[fGpuTracingSupport]);
    r.appendf("Compressed Update Support          : %s\n", gNY[fCompressedTexSubImageSupport]);

    r.appendf("Draw Instead of Clear [workaround] : %s\n", gNY[fUseDrawInsteadOfClear]);

    r.appendf("Max Texture Size                   : %d\n", fMaxTextureSize);
    r.appendf("Max Render Target Size             : %d\n", fMaxRenderTargetSize);
    r.appendf("Max Sample Count                   : %d\n", fMaxSampleCount);

    r.appendf("Map Buffer Support                 : %s\n",
              map_flags_to_string(fMapBufferFlags).c_str());

    static const char* kConfigNames[] = {
        "Unknown",  // kUnknown_GrPixelConfig
        "Alpha8",   // kAlpha_8_GrPixelConfig,
        "Index8",   // kIndex_8_GrPixelConfig,
        "RGB565",   // kRGB_565_GrPixelConfig,
        "RGBA444",  // kRGBA_4444_GrPixelConfig,
        "RGBA8888", // kRGBA_8888_GrPixelConfig,
        "BGRA8888", // kBGRA_8888_GrPixelConfig,
        "ETC1",     // kETC1_GrPixelConfig,
        "LATC",     // kLATC_GrPixelConfig,
        "R11EAC",   // kR11_EAC_GrPixelConfig,
        "ASTC12x12",// kASTC_12x12_GrPixelConfig,
        "RGBAFloat",  // kRGBA_float_GrPixelConfig
    };
    GR_STATIC_ASSERT(0  == kUnknown_GrPixelConfig);
    GR_STATIC_ASSERT(1  == kAlpha_8_GrPixelConfig);
    GR_STATIC_ASSERT(2  == kIndex_8_GrPixelConfig);
    GR_STATIC_ASSERT(3  == kRGB_565_GrPixelConfig);
    GR_STATIC_ASSERT(4  == kRGBA_4444_GrPixelConfig);
    GR_STATIC_ASSERT(5  == kRGBA_8888_GrPixelConfig);
    GR_STATIC_ASSERT(6  == kBGRA_8888_GrPixelConfig);
    GR_STATIC_ASSERT(7  == kETC1_GrPixelConfig);
    GR_STATIC_ASSERT(8  == kLATC_GrPixelConfig);
    GR_STATIC_ASSERT(9  == kR11_EAC_GrPixelConfig);
    GR_STATIC_ASSERT(10 == kASTC_12x12_GrPixelConfig);
    GR_STATIC_ASSERT(11 == kRGBA_float_GrPixelConfig);
    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kConfigNames) == kGrPixelConfigCnt);

    SkASSERT(!fConfigRenderSupport[kUnknown_GrPixelConfig][0]);
    SkASSERT(!fConfigRenderSupport[kUnknown_GrPixelConfig][1]);

    for (size_t i = 1; i < SK_ARRAY_COUNT(kConfigNames); ++i)  {
        r.appendf("%s is renderable: %s, with MSAA: %s\n",
                  kConfigNames[i],
                  gNY[fConfigRenderSupport[i][0]],
                  gNY[fConfigRenderSupport[i][1]]);
    }

    SkASSERT(!fConfigTextureSupport[kUnknown_GrPixelConfig]);

    for (size_t i = 1; i < SK_ARRAY_COUNT(kConfigNames); ++i)  {
        r.appendf("%s is uploadable to a texture: %s\n",
                  kConfigNames[i],
                  gNY[fConfigTextureSupport[i]]);
    }

    return r;
}
Exemple #20
0
static bool dump_png(SkBitmap bitmap, const char* path, const char* md5) {
    const int w = bitmap.width(),
              h = bitmap.height();

    // First get the bitmap into N32 color format.  The next step will work only there.
    if (bitmap.colorType() != kN32_SkColorType) {
        SkBitmap n32;
        if (!bitmap.copyTo(&n32, kN32_SkColorType)) {
            return false;
        }
        bitmap = n32;
    }

    // Convert our N32 bitmap into unpremul RGBA for libpng.
    SkAutoTMalloc<uint32_t> rgba(w*h);
    if (!bitmap.readPixels(SkImageInfo::Make(w,h, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType),
                           rgba, 4*w, 0,0)) {
        return false;
    }

    // We don't need bitmap anymore.  Might as well drop our ref.
    bitmap.reset();

    FILE* f = fopen(path, "wb");
    if (!f) { return false; }

    png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
    if (!png) {
        fclose(f);
        return false;
    }

    png_infop info = png_create_info_struct(png);
    if (!info) {
        png_destroy_write_struct(&png, &info);
        fclose(f);
        return false;
    }

    SkString description;
    description.append("Key: ");
    for (int i = 0; i < FLAGS_key.count(); i++) {
        description.appendf("%s ", FLAGS_key[i]);
    }
    description.append("Properties: ");
    for (int i = 0; i < FLAGS_properties.count(); i++) {
        description.appendf("%s ", FLAGS_properties[i]);
    }
    description.appendf("MD5: %s", md5);

    png_text text[2];
    text[0].key = (png_charp)"Author";
    text[0].text = (png_charp)"DM dump_png()";
    text[0].compression = PNG_TEXT_COMPRESSION_NONE;
    text[1].key = (png_charp)"Description";
    text[1].text = (png_charp)description.c_str();
    text[1].compression = PNG_TEXT_COMPRESSION_NONE;
    png_set_text(png, info, text, 2);

    png_init_io(png, f);
    png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8,
                 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    png_write_info(png, info);
    for (int j = 0; j < h; j++) {
        png_bytep row = (png_bytep)(rgba.get() + w*j);
        png_write_rows(png, &row, 1);
    }
    png_write_end(png, info);

    png_destroy_write_struct(&png, &info);
    fclose(f);
    return true;
}
Exemple #21
0
    static void Run(Task* task) {
        SkString name = task->src->name();
        SkString note;
        SkString whyBlacklisted = is_blacklisted(task->sink.tag, task->src.tag,
                                                 task->src.options, name.c_str());
        if (!whyBlacklisted.isEmpty()) {
            note.appendf(" (--blacklist %s)", whyBlacklisted.c_str());
        }
        SkString log;
        WallTimer timer;
        timer.start();
        if (!FLAGS_dryRun && whyBlacklisted.isEmpty()) {
            SkBitmap bitmap;
            SkDynamicMemoryWStream stream;
            if (FLAGS_pre_log) {
                SkDebugf("\nRunning %s->%s", name.c_str(), task->sink.tag);
            }
            start(task->sink.tag, task->src.tag, task->src.options, name.c_str());
            Error err = task->sink->draw(*task->src, &bitmap, &stream, &log);
            if (!err.isEmpty()) {
                timer.end();
                if (err.isFatal()) {
                    fail(SkStringPrintf("%s %s %s %s: %s",
                                        task->sink.tag,
                                        task->src.tag,
                                        task->src.options,
                                        name.c_str(),
                                        err.c_str()));
                } else {
                    note.appendf(" (skipped: %s)", err.c_str());
                }
                done(timer.fWall, task->sink.tag, task->src.tag, task->src.options,
                     name, note, log);
                return;
            }
            SkAutoTDelete<SkStreamAsset> data(stream.detachAsStream());

            SkString md5;
            if (!FLAGS_writePath.isEmpty() || !FLAGS_readPath.isEmpty()) {
                SkMD5 hash;
                if (data->getLength()) {
                    hash.writeStream(data, data->getLength());
                    data->rewind();
                } else {
                    // If we're BGRA (Linux, Windows), swizzle over to RGBA (Mac, Android).
                    // This helps eliminate multiple 0-pixel-diff hashes on gold.skia.org.
                    // (Android's general slow speed breaks the tie arbitrarily in RGBA's favor.)
                    // We might consider promoting 565 to RGBA too.
                    if (bitmap.colorType() == kBGRA_8888_SkColorType) {
                        SkBitmap swizzle;
                        SkAssertResult(bitmap.copyTo(&swizzle, kRGBA_8888_SkColorType));
                        hash.write(swizzle.getPixels(), swizzle.getSize());
                    } else {
                        hash.write(bitmap.getPixels(), bitmap.getSize());
                    }
                }
                SkMD5::Digest digest;
                hash.finish(digest);
                for (int i = 0; i < 16; i++) {
                    md5.appendf("%02x", digest.data[i]);
                }
            }

            if (!FLAGS_readPath.isEmpty() &&
                !gGold.contains(Gold(task->sink.tag, task->src.tag,
                                     task->src.options, name, md5))) {
                fail(SkStringPrintf("%s not found for %s %s %s %s in %s",
                                    md5.c_str(),
                                    task->sink.tag,
                                    task->src.tag,
                                    task->src.options,
                                    name.c_str(),
                                    FLAGS_readPath[0]));
            }

            if (!FLAGS_writePath.isEmpty()) {
                const char* ext = task->sink->fileExtension();
                if (data->getLength()) {
                    WriteToDisk(*task, md5, ext, data, data->getLength(), NULL);
                    SkASSERT(bitmap.drawsNothing());
                } else if (!bitmap.drawsNothing()) {
                    WriteToDisk(*task, md5, ext, NULL, 0, &bitmap);
                }
            }
        }
        timer.end();
        done(timer.fWall, task->sink.tag, task->src.tag, task->src.options, name, note, log);
    }
Exemple #22
0
    virtual void onDrawContent(SkCanvas* canvas) {
        struct FillAndName {
            SkPath::FillType fFill;
            const char*      fName;
        };
        static const FillAndName gFills[] = {
            {SkPath::kWinding_FillType, "Winding"},
            {SkPath::kEvenOdd_FillType, "Even / Odd"},
            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
        };
        struct StyleAndName {
            SkPaint::Style fStyle;
            const char*    fName;
        };
        static const StyleAndName gStyles[] = {
            {SkPaint::kFill_Style, "Fill"},
            {SkPaint::kStroke_Style, "Stroke"},
            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
        };

        SkPaint titlePaint;
        titlePaint.setColor(SK_ColorBLACK);
        titlePaint.setAntiAlias(true);
        titlePaint.setLCDRenderText(true);
        titlePaint.setTextSize(24 * SK_Scalar1);
        const char title[] = "Empty Paths Drawn Into Rectangle Clips With Indicated Style and Fill";
        canvas->drawText(title, strlen(title),
                         40 * SK_Scalar1,
                         100*SK_Scalar1,
                         titlePaint);

        SkRandom rand;
        SkRect rect = SkRect::MakeWH(125*SK_Scalar1, 100*SK_Scalar1);
        int i = 0;
        canvas->save();
        canvas->translate(80 * SK_Scalar1, 0);
        canvas->save();
        for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
                if (0 == i % 4) {
                    canvas->restore();
                    canvas->translate(0, rect.height() + 50 * SK_Scalar1);
                    canvas->save();
                } else {
                    canvas->translate(rect.width() + 100 * SK_Scalar1, 0);
                }
                ++i;


                SkColor color = rand.nextU();
                color = 0xff000000| color; // force solid
                this->drawEmpty(canvas, color, rect,
                                gStyles[style].fStyle, gFills[fill].fFill);

                SkPaint rectPaint;
                rectPaint.setColor(SK_ColorBLACK);
                rectPaint.setStyle(SkPaint::kStroke_Style);
                rectPaint.setStrokeWidth(-1);
                rectPaint.setAntiAlias(true);
                canvas->drawRect(rect, rectPaint);

                SkString label;
                label.appendf("%s, %s", gStyles[style].fName, gFills[fill].fName);

                SkPaint labelPaint;
                labelPaint.setColor(color);
                labelPaint.setAntiAlias(true);
                labelPaint.setLCDRenderText(true);
                canvas->drawText(label.c_str(), label.size(),
                                 0, rect.height() + 15 * SK_Scalar1,
                                 labelPaint);
            }
        }
        canvas->restore();
        canvas->restore();
    }
Exemple #23
0
SkString* SkObjectParser::PathToString(const SkPath& path) {
    SkString* mPath = new SkString;
    
    mPath->appendf("Path (%d) (", path.getGenerationID());

    static const char* gFillStrings[] = {
        "Winding", "EvenOdd", "InverseWinding", "InverseEvenOdd"
    };

    mPath->append(gFillStrings[path.getFillType()]);
    mPath->append(", ");

    static const char* gConvexityStrings[] = {
        "Unknown", "Convex", "Concave"
    };
    SkASSERT(SkPath::kConcave_Convexity == 2);

    mPath->append(gConvexityStrings[path.getConvexity()]);
    mPath->append(", ");

    if (path.isRect(nullptr)) {
        mPath->append("isRect, ");
    } else {
        mPath->append("isNotRect, ");
    }

    if (path.isOval(nullptr)) {
        mPath->append("isOval, ");
    } else {
        mPath->append("isNotOval, ");
    }

    SkRRect rrect;
    if (path.isRRect(&rrect)) {
        mPath->append("isRRect, ");
    } else {
        mPath->append("isNotRRect, ");
    }

    mPath->appendS32(path.countVerbs());
    mPath->append("V, ");
    mPath->appendS32(path.countPoints());
    mPath->append("P): ");

    static const char* gVerbStrings[] = {
        "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done"
    };
    static const int gPtsPerVerb[] = { 1, 1, 2, 2, 3, 0, 0 };
    static const int gPtOffsetPerVerb[] = { 0, 1, 1, 1, 1, 0, 0 };
    SkASSERT(SkPath::kDone_Verb == 6);

    SkPath::Iter iter(const_cast<SkPath&>(path), false);
    SkPath::Verb verb;
    SkPoint points[4];

    for(verb = iter.next(points, false);
        verb != SkPath::kDone_Verb;
        verb = iter.next(points, false)) {

        mPath->append(gVerbStrings[verb]);
        mPath->append(" ");

        for (int i = 0; i < gPtsPerVerb[verb]; ++i) {
            mPath->append("(");
            mPath->appendScalar(points[gPtOffsetPerVerb[verb]+i].fX);
            mPath->append(", ");
            mPath->appendScalar(points[gPtOffsetPerVerb[verb]+i].fY);
            mPath->append(")");
        }

        if (SkPath::kConic_Verb == verb) {
            mPath->append("(");
            mPath->appendScalar(iter.conicWeight());
            mPath->append(")");
        }

        mPath->append(" ");
    }

    SkString* boundStr = SkObjectParser::RectToString(path.getBounds(), "    Bound: ");

    if (boundStr) {
        mPath->append(*boundStr);
        delete boundStr;
    }

    return mPath;
}
Exemple #24
0
SkString UnderJoin(const char* a, const char* b) {
    SkString s;
    s.appendf("%s_%s", a, b);
    return s;
}
Exemple #25
0
SkString GrDrawTargetCaps::dump() const {
    SkString r;
    static const char* gNY[] = {"NO", "YES"};
    r.appendf("8 Bit Palette Support        : %s\n", gNY[f8BitPaletteSupport]);
    r.appendf("MIP Map Support              : %s\n", gNY[fMipMapSupport]);
    r.appendf("NPOT Texture Tile Support    : %s\n", gNY[fNPOTTextureTileSupport]);
    r.appendf("Two Sided Stencil Support    : %s\n", gNY[fTwoSidedStencilSupport]);
    r.appendf("Stencil Wrap Ops  Support    : %s\n", gNY[fStencilWrapOpsSupport]);
    r.appendf("HW AA Lines Support          : %s\n", gNY[fHWAALineSupport]);
    r.appendf("Shader Derivative Support    : %s\n", gNY[fShaderDerivativeSupport]);
    r.appendf("Geometry Shader Support      : %s\n", gNY[fGeometryShaderSupport]);
    r.appendf("Dual Source Blending Support : %s\n", gNY[fDualSourceBlendingSupport]);
    r.appendf("Buffer Lock Support          : %s\n", gNY[fBufferLockSupport]);
    r.appendf("Path Rendering Support       : %s\n", gNY[fPathRenderingSupport]);
    r.appendf("Dst Read In Shader Support   : %s\n", gNY[fDstReadInShaderSupport]);
    r.appendf("Discard Render Target Support: %s\n", gNY[fDiscardRenderTargetSupport]);
    r.appendf("Reuse Scratch Textures       : %s\n", gNY[fReuseScratchTextures]);
    r.appendf("Gpu Tracing Support          : %s\n", gNY[fGpuTracingSupport]);
    r.appendf("Max Texture Size             : %d\n", fMaxTextureSize);
    r.appendf("Max Render Target Size       : %d\n", fMaxRenderTargetSize);
    r.appendf("Max Sample Count             : %d\n", fMaxSampleCount);

    static const char* kConfigNames[] = {
        "Unknown",  // kUnknown_GrPixelConfig
        "Alpha8",   // kAlpha_8_GrPixelConfig,
        "Index8",   // kIndex_8_GrPixelConfig,
        "RGB565",   // kRGB_565_GrPixelConfig,
        "RGBA444",  // kRGBA_4444_GrPixelConfig,
        "RGBA8888", // kRGBA_8888_GrPixelConfig,
        "BGRA8888", // kBGRA_8888_GrPixelConfig,
    };
    GR_STATIC_ASSERT(0 == kUnknown_GrPixelConfig);
    GR_STATIC_ASSERT(1 == kAlpha_8_GrPixelConfig);
    GR_STATIC_ASSERT(2 == kIndex_8_GrPixelConfig);
    GR_STATIC_ASSERT(3 == kRGB_565_GrPixelConfig);
    GR_STATIC_ASSERT(4 == kRGBA_4444_GrPixelConfig);
    GR_STATIC_ASSERT(5 == kRGBA_8888_GrPixelConfig);
    GR_STATIC_ASSERT(6 == kBGRA_8888_GrPixelConfig);
    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kConfigNames) == kGrPixelConfigCnt);

    SkASSERT(!fConfigRenderSupport[kUnknown_GrPixelConfig][0]);
    SkASSERT(!fConfigRenderSupport[kUnknown_GrPixelConfig][1]);
    for (size_t i = 0; i < SK_ARRAY_COUNT(kConfigNames); ++i)  {
        if (i != kUnknown_GrPixelConfig) {
            r.appendf("%s is renderable: %s, with MSAA: %s\n",
                     kConfigNames[i],
                     gNY[fConfigRenderSupport[i][0]],
                     gNY[fConfigRenderSupport[i][1]]);
        }
    }
    return r;
}
    void onDraw(SkCanvas* canvas) override {
        SkRect dstRect = { 0, 0, SkIntToScalar(64), SkIntToScalar(64)};
        static const int kMaxSrcRectSize = 1 << (SkNextLog2(gBmpSize) + 2);

        static const int kPadX = 30;
        static const int kPadY = 40;
        SkPaint paint;
        paint.setAlpha(0x20);
        canvas->drawBitmapRect(fLargeBitmap, SkRect::MakeIWH(gSize, gSize), &paint);
        canvas->translate(SK_Scalar1 * kPadX / 2,
                          SK_Scalar1 * kPadY / 2);
        SkPaint blackPaint;
        SkScalar titleHeight = SK_Scalar1 * 24;
        blackPaint.setColor(SK_ColorBLACK);
        blackPaint.setTextSize(titleHeight);
        blackPaint.setAntiAlias(true);
        sk_tool_utils::set_portable_typeface(&blackPaint);
        SkString title;
        title.printf("Bitmap size: %d x %d", gBmpSize, gBmpSize);
        canvas->drawText(title.c_str(), title.size(), 0,
                         titleHeight, blackPaint);

        canvas->translate(0, SK_Scalar1 * kPadY / 2  + titleHeight);
        int rowCount = 0;
        canvas->save();
        for (int w = 1; w <= kMaxSrcRectSize; w *= 4) {
            for (int h = 1; h <= kMaxSrcRectSize; h *= 4) {

                SkIRect srcRect = SkIRect::MakeXYWH((gBmpSize - w) / 2, (gBmpSize - h) / 2, w, h);
                fProc(canvas, fImage, fLargeBitmap, srcRect, dstRect);

                SkString label;
                label.appendf("%d x %d", w, h);
                blackPaint.setAntiAlias(true);
                blackPaint.setStyle(SkPaint::kFill_Style);
                blackPaint.setTextSize(SK_Scalar1 * 10);
                SkScalar baseline = dstRect.height() +
                                    blackPaint.getTextSize() + SK_Scalar1 * 3;
                canvas->drawText(label.c_str(), label.size(),
                                    0, baseline,
                                    blackPaint);
                blackPaint.setStyle(SkPaint::kStroke_Style);
                blackPaint.setStrokeWidth(SK_Scalar1);
                blackPaint.setAntiAlias(false);
                canvas->drawRect(dstRect, blackPaint);

                canvas->translate(dstRect.width() + SK_Scalar1 * kPadX, 0);
                ++rowCount;
                if ((dstRect.width() + kPadX) * rowCount > gSize) {
                    canvas->restore();
                    canvas->translate(0, dstRect.height() + SK_Scalar1 * kPadY);
                    canvas->save();
                    rowCount = 0;
                }
            }
        }

        {
            // test the following code path:
            // SkGpuDevice::drawPath() -> SkGpuDevice::drawWithMaskFilter()
            SkIRect srcRect;
            SkPaint paint;
            SkBitmap bm;

            bm = make_chessbm(5, 5);
            paint.setFilterQuality(kLow_SkFilterQuality);

            srcRect.setXYWH(1, 1, 3, 3);
            SkMaskFilter* mf = SkBlurMaskFilter::Create(
                kNormal_SkBlurStyle,
                SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)),
                SkBlurMaskFilter::kHighQuality_BlurFlag |
                SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
            paint.setMaskFilter(mf)->unref();
            canvas->drawBitmapRect(bm, srcRect, dstRect, &paint);
        }
    }
Exemple #27
0
void GrGLPerlinNoise::emitCode(GrGLFPBuilder* builder,
                               const GrFragmentProcessor&,
                               const char* outputColor,
                               const char* inputColor,
                               const TransformedCoordsArray& coords,
                               const TextureSamplerArray& samplers) {
    sk_ignore_unused_variable(inputColor);

    GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
    SkString vCoords = fsBuilder->ensureFSCoords2D(coords, 0);

    fBaseFrequencyUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
                                            kVec2f_GrSLType, kDefault_GrSLPrecision,
                                            "baseFrequency");
    const char* baseFrequencyUni = builder->getUniformCStr(fBaseFrequencyUni);
    fAlphaUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
                                    kFloat_GrSLType, kDefault_GrSLPrecision,
                                    "alpha");
    const char* alphaUni = builder->getUniformCStr(fAlphaUni);

    const char* stitchDataUni = NULL;
    if (fStitchTiles) {
        fStitchDataUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
                                             kVec2f_GrSLType, kDefault_GrSLPrecision,
                                             "stitchData");
        stitchDataUni = builder->getUniformCStr(fStitchDataUni);
    }

    // There are 4 lines, so the center of each line is 1/8, 3/8, 5/8 and 7/8
    const char* chanCoordR  = "0.125";
    const char* chanCoordG  = "0.375";
    const char* chanCoordB  = "0.625";
    const char* chanCoordA  = "0.875";
    const char* chanCoord   = "chanCoord";
    const char* stitchData  = "stitchData";
    const char* ratio       = "ratio";
    const char* noiseVec    = "noiseVec";
    const char* noiseSmooth = "noiseSmooth";
    const char* floorVal    = "floorVal";
    const char* fractVal    = "fractVal";
    const char* uv          = "uv";
    const char* ab          = "ab";
    const char* latticeIdx  = "latticeIdx";
    const char* bcoords     = "bcoords";
    const char* lattice     = "lattice";
    const char* inc8bit     = "0.00390625";  // 1.0 / 256.0
    // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a
    // [-1,1] vector and perform a dot product between that vector and the provided vector.
    const char* dotLattice  = "dot(((%s.ga + %s.rb * vec2(%s)) * vec2(2.0) - vec2(1.0)), %s);";

    // Add noise function
    static const GrGLShaderVar gPerlinNoiseArgs[] =  {
        GrGLShaderVar(chanCoord, kFloat_GrSLType),
        GrGLShaderVar(noiseVec, kVec2f_GrSLType)
    };

    static const GrGLShaderVar gPerlinNoiseStitchArgs[] =  {
        GrGLShaderVar(chanCoord, kFloat_GrSLType),
        GrGLShaderVar(noiseVec, kVec2f_GrSLType),
        GrGLShaderVar(stitchData, kVec2f_GrSLType)
    };

    SkString noiseCode;

    noiseCode.appendf("\tvec4 %s;\n", floorVal);
    noiseCode.appendf("\t%s.xy = floor(%s);\n", floorVal, noiseVec);
    noiseCode.appendf("\t%s.zw = %s.xy + vec2(1.0);\n", floorVal, floorVal);
    noiseCode.appendf("\tvec2 %s = fract(%s);\n", fractVal, noiseVec);

    // smooth curve : t * t * (3 - 2 * t)
    noiseCode.appendf("\n\tvec2 %s = %s * %s * (vec2(3.0) - vec2(2.0) * %s);",
        noiseSmooth, fractVal, fractVal, fractVal);

    // Adjust frequencies if we're stitching tiles
    if (fStitchTiles) {
        noiseCode.appendf("\n\tif(%s.x >= %s.x) { %s.x -= %s.x; }",
            floorVal, stitchData, floorVal, stitchData);
        noiseCode.appendf("\n\tif(%s.y >= %s.y) { %s.y -= %s.y; }",
            floorVal, stitchData, floorVal, stitchData);
        noiseCode.appendf("\n\tif(%s.z >= %s.x) { %s.z -= %s.x; }",
            floorVal, stitchData, floorVal, stitchData);
        noiseCode.appendf("\n\tif(%s.w >= %s.y) { %s.w -= %s.y; }",
            floorVal, stitchData, floorVal, stitchData);
    }

    // Get texture coordinates and normalize
    noiseCode.appendf("\n\t%s = fract(floor(mod(%s, 256.0)) / vec4(256.0));\n",
        floorVal, floorVal);

    // Get permutation for x
    {
        SkString xCoords("");
        xCoords.appendf("vec2(%s.x, 0.5)", floorVal);

        noiseCode.appendf("\n\tvec2 %s;\n\t%s.x = ", latticeIdx, latticeIdx);
        fsBuilder->appendTextureLookup(&noiseCode, samplers[0], xCoords.c_str(), kVec2f_GrSLType);
        noiseCode.append(".r;");
    }

    // Get permutation for x + 1
    {
        SkString xCoords("");
        xCoords.appendf("vec2(%s.z, 0.5)", floorVal);

        noiseCode.appendf("\n\t%s.y = ", latticeIdx);
        fsBuilder->appendTextureLookup(&noiseCode, samplers[0], xCoords.c_str(), kVec2f_GrSLType);
        noiseCode.append(".r;");
    }

#if defined(SK_BUILD_FOR_ANDROID)
    // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3).
    // The issue is that colors aren't accurate enough on Tegra devices. For example, if an 8 bit
    // value of 124 (or 0.486275 here) is entered, we can get a texture value of 123.513725
    // (or 0.484368 here). The following rounding operation prevents these precision issues from
    // affecting the result of the noise by making sure that we only have multiples of 1/255.
    // (Note that 1/255 is about 0.003921569, which is the value used here).
    noiseCode.appendf("\n\t%s = floor(%s * vec2(255.0) + vec2(0.5)) * vec2(0.003921569);",
                      latticeIdx, latticeIdx);
#endif

    // Get (x,y) coordinates with the permutated x
    noiseCode.appendf("\n\tvec4 %s = fract(%s.xyxy + %s.yyww);", bcoords, latticeIdx, floorVal);

    noiseCode.appendf("\n\n\tvec2 %s;", uv);
    // Compute u, at offset (0,0)
    {
        SkString latticeCoords("");
        latticeCoords.appendf("vec2(%s.x, %s)", bcoords, chanCoord);
        noiseCode.appendf("\n\tvec4 %s = ", lattice);
        fsBuilder->appendTextureLookup(&noiseCode, samplers[1], latticeCoords.c_str(),
            kVec2f_GrSLType);
        noiseCode.appendf(".bgra;\n\t%s.x = ", uv);
        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
    }

    noiseCode.appendf("\n\t%s.x -= 1.0;", fractVal);
    // Compute v, at offset (-1,0)
    {
        SkString latticeCoords("");
        latticeCoords.appendf("vec2(%s.y, %s)", bcoords, chanCoord);
        noiseCode.append("\n\tlattice = ");
        fsBuilder->appendTextureLookup(&noiseCode, samplers[1], latticeCoords.c_str(),
            kVec2f_GrSLType);
        noiseCode.appendf(".bgra;\n\t%s.y = ", uv);
        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
    }

    // Compute 'a' as a linear interpolation of 'u' and 'v'
    noiseCode.appendf("\n\tvec2 %s;", ab);
    noiseCode.appendf("\n\t%s.x = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);

    noiseCode.appendf("\n\t%s.y -= 1.0;", fractVal);
    // Compute v, at offset (-1,-1)
    {
        SkString latticeCoords("");
        latticeCoords.appendf("vec2(%s.w, %s)", bcoords, chanCoord);
        noiseCode.append("\n\tlattice = ");
        fsBuilder->appendTextureLookup(&noiseCode, samplers[1], latticeCoords.c_str(),
            kVec2f_GrSLType);
        noiseCode.appendf(".bgra;\n\t%s.y = ", uv);
        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
    }

    noiseCode.appendf("\n\t%s.x += 1.0;", fractVal);
    // Compute u, at offset (0,-1)
    {
        SkString latticeCoords("");
        latticeCoords.appendf("vec2(%s.z, %s)", bcoords, chanCoord);
        noiseCode.append("\n\tlattice = ");
        fsBuilder->appendTextureLookup(&noiseCode, samplers[1], latticeCoords.c_str(),
            kVec2f_GrSLType);
        noiseCode.appendf(".bgra;\n\t%s.x = ", uv);
        noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal);
    }

    // Compute 'b' as a linear interpolation of 'u' and 'v'
    noiseCode.appendf("\n\t%s.y = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth);
    // Compute the noise as a linear interpolation of 'a' and 'b'
    noiseCode.appendf("\n\treturn mix(%s.x, %s.y, %s.y);\n", ab, ab, noiseSmooth);

    SkString noiseFuncName;
    if (fStitchTiles) {
        fsBuilder->emitFunction(kFloat_GrSLType,
                                "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseStitchArgs),
                                gPerlinNoiseStitchArgs, noiseCode.c_str(), &noiseFuncName);
    } else {
        fsBuilder->emitFunction(kFloat_GrSLType,
                                "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseArgs),
                                gPerlinNoiseArgs, noiseCode.c_str(), &noiseFuncName);
    }

    // There are rounding errors if the floor operation is not performed here
    fsBuilder->codeAppendf("\n\t\tvec2 %s = floor(%s.xy) * %s;",
                           noiseVec, vCoords.c_str(), baseFrequencyUni);

    // Clear the color accumulator
    fsBuilder->codeAppendf("\n\t\t%s = vec4(0.0);", outputColor);

    if (fStitchTiles) {
        // Set up TurbulenceInitial stitch values.
        fsBuilder->codeAppendf("\n\t\tvec2 %s = %s;", stitchData, stitchDataUni);
    }

    fsBuilder->codeAppendf("\n\t\tfloat %s = 1.0;", ratio);

    // Loop over all octaves
    fsBuilder->codeAppendf("\n\t\tfor (int octave = 0; octave < %d; ++octave) {", fNumOctaves);

    fsBuilder->codeAppendf("\n\t\t\t%s += ", outputColor);
    if (fType != SkPerlinNoiseShader::kFractalNoise_Type) {
        fsBuilder->codeAppend("abs(");
    }
    if (fStitchTiles) {
        fsBuilder->codeAppendf(
            "vec4(\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s),"
                 "\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s))",
            noiseFuncName.c_str(), chanCoordR, noiseVec, stitchData,
            noiseFuncName.c_str(), chanCoordG, noiseVec, stitchData,
            noiseFuncName.c_str(), chanCoordB, noiseVec, stitchData,
            noiseFuncName.c_str(), chanCoordA, noiseVec, stitchData);
    } else {
        fsBuilder->codeAppendf(
            "vec4(\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s),"
                 "\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s))",
            noiseFuncName.c_str(), chanCoordR, noiseVec,
            noiseFuncName.c_str(), chanCoordG, noiseVec,
            noiseFuncName.c_str(), chanCoordB, noiseVec,
            noiseFuncName.c_str(), chanCoordA, noiseVec);
    }
    if (fType != SkPerlinNoiseShader::kFractalNoise_Type) {
        fsBuilder->codeAppendf(")"); // end of "abs("
    }
    fsBuilder->codeAppendf(" * %s;", ratio);

    fsBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", noiseVec);
    fsBuilder->codeAppendf("\n\t\t\t%s *= 0.5;", ratio);

    if (fStitchTiles) {
        fsBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", stitchData);
    }
    fsBuilder->codeAppend("\n\t\t}"); // end of the for loop on octaves

    if (fType == SkPerlinNoiseShader::kFractalNoise_Type) {
        // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
        // by fractalNoise and (turbulenceFunctionResult) by turbulence.
        fsBuilder->codeAppendf("\n\t\t%s = %s * vec4(0.5) + vec4(0.5);", outputColor, outputColor);
    }

    fsBuilder->codeAppendf("\n\t\t%s.a *= %s;", outputColor, alphaUni);

    // Clamp values
    fsBuilder->codeAppendf("\n\t\t%s = clamp(%s, 0.0, 1.0);", outputColor, outputColor);

    // Pre-multiply the result
    fsBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n",
                  outputColor, outputColor, outputColor, outputColor);
}
Exemple #28
0
    bool CopyTilesRenderer::render(SkBitmap** out) {
        int i = 0;
        bool success = true;
        SkBitmap dst;
        for (int x = 0; x < this->getViewWidth(); x += fLargeTileWidth) {
            for (int y = 0; y < this->getViewHeight(); y += fLargeTileHeight) {
                SkAutoCanvasRestore autoRestore(fCanvas, true);
                // Translate so that we draw the correct portion of the picture.
                // Perform a postTranslate so that the scaleFactor does not interfere with the
                // positioning.
                SkMatrix mat(fCanvas->getTotalMatrix());
                mat.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
                fCanvas->setMatrix(mat);
                // Draw the picture
                if (fUseMultiPictureDraw) {
                    SkMultiPictureDraw mpd;

                    mpd.add(fCanvas, fPicture);

                    mpd.draw();
                } else {
                    fCanvas->drawPicture(fPicture);
                }
                // Now extract the picture into tiles
                SkBitmap baseBitmap;
                fCanvas->readPixels(SkIRect::MakeSize(fCanvas->getBaseLayerSize()), &baseBitmap);
                SkIRect subset;
                for (int tileY = 0; tileY < fLargeTileHeight; tileY += this->getTileHeight()) {
                    for (int tileX = 0; tileX < fLargeTileWidth; tileX += this->getTileWidth()) {
                        subset.set(tileX, tileY, tileX + this->getTileWidth(),
                                   tileY + this->getTileHeight());
                        SkDEBUGCODE(bool extracted =)
                        baseBitmap.extractSubset(&dst, subset);
                        SkASSERT(extracted);
                        if (!fWritePath.isEmpty()) {
                            // Similar to write() in PictureRenderer.cpp, but just encodes
                            // a bitmap directly.
                            // TODO: Share more common code with write() to do this, to properly
                            // write out the JSON summary, etc.
                            SkString pathWithNumber = SkOSPath::Join(fWritePath.c_str(),
                                                                     fInputFilename.c_str());
                            pathWithNumber.remove(pathWithNumber.size() - 4, 4);
                            pathWithNumber.appendf("%i.png", i++);
                            SkBitmap copy;
#if SK_SUPPORT_GPU
                            if (isUsingGpuDevice()) {
                                dst.pixelRef()->readPixels(&copy, &subset);
                            } else {
#endif
                                dst.copyTo(&copy);
#if SK_SUPPORT_GPU
                            }
#endif
                            success &= SkImageEncoder::EncodeFile(pathWithNumber.c_str(), copy,
                                                                  SkImageEncoder::kPNG_Type, 100);
                        }
                    }
                }
            }
        }
        return success;
    }
Exemple #29
0
SkString GrCaps::dump() const {
    SkString r;
    static const char* gNY[] = {"NO", "YES"};
    r.appendf("MIP Map Support                    : %s\n", gNY[fMipMapSupport]);
    r.appendf("NPOT Texture Tile Support          : %s\n", gNY[fNPOTTextureTileSupport]);
    r.appendf("sRGB Support                       : %s\n", gNY[fSRGBSupport]);
    r.appendf("Two Sided Stencil Support          : %s\n", gNY[fTwoSidedStencilSupport]);
    r.appendf("Stencil Wrap Ops  Support          : %s\n", gNY[fStencilWrapOpsSupport]);
    r.appendf("Discard Render Target Support      : %s\n", gNY[fDiscardRenderTargetSupport]);
    r.appendf("Reuse Scratch Textures             : %s\n", gNY[fReuseScratchTextures]);
    r.appendf("Reuse Scratch Buffers              : %s\n", gNY[fReuseScratchBuffers]);
    r.appendf("Gpu Tracing Support                : %s\n", gNY[fGpuTracingSupport]);
    r.appendf("Compressed Update Support          : %s\n", gNY[fCompressedTexSubImageSupport]);
    r.appendf("Oversized Stencil Support          : %s\n", gNY[fOversizedStencilSupport]);
    r.appendf("Texture Barrier Support            : %s\n", gNY[fTextureBarrierSupport]);
    r.appendf("Sample Locations Support           : %s\n", gNY[fSampleLocationsSupport]);
    r.appendf("Uses Mixed Samples                 : %s\n", gNY[fUsesMixedSamples]);
    r.appendf("Supports instanced draws           : %s\n", gNY[fSupportsInstancedDraws]);
    r.appendf("Full screen clear is free          : %s\n", gNY[fFullClearIsFree]);
    r.appendf("Must clear buffer memory           : %s\n", gNY[fMustClearUploadedBufferData]);
    r.appendf("Draw Instead of Clear [workaround] : %s\n", gNY[fUseDrawInsteadOfClear]);
    r.appendf("Draw Instead of TexSubImage [workaround] : %s\n",
              gNY[fUseDrawInsteadOfPartialRenderTargetWrite]);
    r.appendf("Prefer VRAM Use over flushes [workaround] : %s\n", gNY[fPreferVRAMUseOverFlushes]);

    if (this->advancedBlendEquationSupport()) {
        r.appendf("Advanced Blend Equation Blacklist  : 0x%x\n", fAdvBlendEqBlacklist);
    }

    r.appendf("Max Vertex Attributes              : %d\n", fMaxVertexAttributes);
    r.appendf("Max Texture Size                   : %d\n", fMaxTextureSize);
    r.appendf("Max Render Target Size             : %d\n", fMaxRenderTargetSize);
    r.appendf("Max Color Sample Count             : %d\n", fMaxColorSampleCount);
    r.appendf("Max Stencil Sample Count           : %d\n", fMaxStencilSampleCount);
    r.appendf("Max Raster Samples                 : %d\n", fMaxRasterSamples);

    static const char* kBlendEquationSupportNames[] = {
        "Basic",
        "Advanced",
        "Advanced Coherent",
    };
    GR_STATIC_ASSERT(0 == kBasic_BlendEquationSupport);
    GR_STATIC_ASSERT(1 == kAdvanced_BlendEquationSupport);
    GR_STATIC_ASSERT(2 == kAdvancedCoherent_BlendEquationSupport);
    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kBlendEquationSupportNames) == kLast_BlendEquationSupport + 1);

    r.appendf("Blend Equation Support             : %s\n",
              kBlendEquationSupportNames[fBlendEquationSupport]);
    r.appendf("Map Buffer Support                 : %s\n",
              map_flags_to_string(fMapBufferFlags).c_str());

    static const char* kConfigNames[] = {
        "Unknown",  // kUnknown_GrPixelConfig
        "Alpha8",   // kAlpha_8_GrPixelConfig,
        "Index8",   // kIndex_8_GrPixelConfig,
        "RGB565",   // kRGB_565_GrPixelConfig,
        "RGBA444",  // kRGBA_4444_GrPixelConfig,
        "RGBA8888", // kRGBA_8888_GrPixelConfig,
        "BGRA8888", // kBGRA_8888_GrPixelConfig,
        "SRGBA8888",// kSRGBA_8888_GrPixelConfig,
        "SBGRA8888",// kSBGRA_8888_GrPixelConfig,
        "ETC1",     // kETC1_GrPixelConfig,
        "LATC",     // kLATC_GrPixelConfig,
        "R11EAC",   // kR11_EAC_GrPixelConfig,
        "ASTC12x12",// kASTC_12x12_GrPixelConfig,
        "RGBAFloat",// kRGBA_float_GrPixelConfig
        "AlphaHalf",// kAlpha_half_GrPixelConfig
        "RGBAHalf", // kRGBA_half_GrPixelConfig
    };
    GR_STATIC_ASSERT(0  == kUnknown_GrPixelConfig);
    GR_STATIC_ASSERT(1  == kAlpha_8_GrPixelConfig);
    GR_STATIC_ASSERT(2  == kIndex_8_GrPixelConfig);
    GR_STATIC_ASSERT(3  == kRGB_565_GrPixelConfig);
    GR_STATIC_ASSERT(4  == kRGBA_4444_GrPixelConfig);
    GR_STATIC_ASSERT(5  == kRGBA_8888_GrPixelConfig);
    GR_STATIC_ASSERT(6  == kBGRA_8888_GrPixelConfig);
    GR_STATIC_ASSERT(7  == kSRGBA_8888_GrPixelConfig);
    GR_STATIC_ASSERT(8  == kSBGRA_8888_GrPixelConfig);
    GR_STATIC_ASSERT(9  == kETC1_GrPixelConfig);
    GR_STATIC_ASSERT(10  == kLATC_GrPixelConfig);
    GR_STATIC_ASSERT(11  == kR11_EAC_GrPixelConfig);
    GR_STATIC_ASSERT(12 == kASTC_12x12_GrPixelConfig);
    GR_STATIC_ASSERT(13 == kRGBA_float_GrPixelConfig);
    GR_STATIC_ASSERT(14 == kAlpha_half_GrPixelConfig);
    GR_STATIC_ASSERT(15 == kRGBA_half_GrPixelConfig);
    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kConfigNames) == kGrPixelConfigCnt);

    SkASSERT(!this->isConfigRenderable(kUnknown_GrPixelConfig, false));
    SkASSERT(!this->isConfigRenderable(kUnknown_GrPixelConfig, true));

    for (size_t i = 1; i < SK_ARRAY_COUNT(kConfigNames); ++i)  {
        GrPixelConfig config = static_cast<GrPixelConfig>(i);
        r.appendf("%s is renderable: %s, with MSAA: %s\n",
                  kConfigNames[i],
                  gNY[this->isConfigRenderable(config, false)],
                  gNY[this->isConfigRenderable(config, true)]);
    }

    SkASSERT(!this->isConfigTexturable(kUnknown_GrPixelConfig));

    for (size_t i = 1; i < SK_ARRAY_COUNT(kConfigNames); ++i)  {
        GrPixelConfig config = static_cast<GrPixelConfig>(i);
        r.appendf("%s is uploadable to a texture: %s\n",
                  kConfigNames[i],
                  gNY[this->isConfigTexturable(config)]);
    }

    return r;
}
Exemple #30
0
SkString GrGLCaps::dump() const {

    SkString r = INHERITED::dump();

    r.appendf("--- GL-Specific ---\n");
    for (int i = 0; i < fStencilFormats.count(); ++i) {
        r.appendf("Stencil Format %d, stencil bits: %02d, total bits: %02d\n",
                 i,
                 fStencilFormats[i].fStencilBits,
                 fStencilFormats[i].fTotalBits);
    }

    static const char* kMSFBOExtStr[] = {
        "None",
        "ARB",
        "EXT",
        "ES 3.0",
        "Apple",
        "IMG MS To Texture",
        "EXT MS To Texture",
    };
    GR_STATIC_ASSERT(0 == kNone_MSFBOType);
    GR_STATIC_ASSERT(1 == kDesktop_ARB_MSFBOType);
    GR_STATIC_ASSERT(2 == kDesktop_EXT_MSFBOType);
    GR_STATIC_ASSERT(3 == kES_3_0_MSFBOType);
    GR_STATIC_ASSERT(4 == kES_Apple_MSFBOType);
    GR_STATIC_ASSERT(5 == kES_IMG_MsToTexture_MSFBOType);
    GR_STATIC_ASSERT(6 == kES_EXT_MsToTexture_MSFBOType);
    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kMSFBOExtStr) == kLast_MSFBOType + 1);

    static const char* kFBFetchTypeStr[] = {
        "None",
        "EXT",
        "NV",
    };
    GR_STATIC_ASSERT(0 == kNone_FBFetchType);
    GR_STATIC_ASSERT(1 == kEXT_FBFetchType);
    GR_STATIC_ASSERT(2 == kNV_FBFetchType);
    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kFBFetchTypeStr) == kLast_FBFetchType + 1);

    static const char* kInvalidateFBTypeStr[] = {
        "None",
        "Discard",
        "Invalidate",
    };
    GR_STATIC_ASSERT(0 == kNone_InvalidateFBType);
    GR_STATIC_ASSERT(1 == kDiscard_InvalidateFBType);
    GR_STATIC_ASSERT(2 == kInvalidate_InvalidateFBType);
    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kInvalidateFBTypeStr) == kLast_InvalidateFBType + 1);

    static const char* kMapBufferTypeStr[] = {
        "None",
        "MapBuffer",
        "MapBufferRange",
        "Chromium",
    };
    GR_STATIC_ASSERT(0 == kNone_MapBufferType);
    GR_STATIC_ASSERT(1 == kMapBuffer_MapBufferType);
    GR_STATIC_ASSERT(2 == kMapBufferRange_MapBufferType);
    GR_STATIC_ASSERT(3 == kChromium_MapBufferType);
    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kMapBufferTypeStr) == kLast_MapBufferType + 1);

    r.appendf("Core Profile: %s\n", (fIsCoreProfile ? "YES" : "NO"));
    r.appendf("MSAA Type: %s\n", kMSFBOExtStr[fMSFBOType]);
    r.appendf("FB Fetch Type: %s\n", kFBFetchTypeStr[fFBFetchType]);
    r.appendf("Invalidate FB Type: %s\n", kInvalidateFBTypeStr[fInvalidateFBType]);
    r.appendf("Map Buffer Type: %s\n", kMapBufferTypeStr[fMapBufferType]);
    r.appendf("Max FS Uniform Vectors: %d\n", fMaxFragmentUniformVectors);
    r.appendf("Max FS Texture Units: %d\n", fMaxFragmentTextureUnits);
    if (!fIsCoreProfile) {
        r.appendf("Max Fixed Function Texture Coords: %d\n", fMaxFixedFunctionTextureCoords);
    }
    r.appendf("Max Vertex Attributes: %d\n", fMaxVertexAttributes);
    r.appendf("Support RGBA8 Render Buffer: %s\n", (fRGBA8RenderbufferSupport ? "YES": "NO"));
    r.appendf("BGRA is an internal format: %s\n", (fBGRAIsInternalFormat ? "YES": "NO"));
    r.appendf("Support texture swizzle: %s\n", (fTextureSwizzleSupport ? "YES": "NO"));
    r.appendf("Unpack Row length support: %s\n", (fUnpackRowLengthSupport ? "YES": "NO"));
    r.appendf("Unpack Flip Y support: %s\n", (fUnpackFlipYSupport ? "YES": "NO"));
    r.appendf("Pack Row length support: %s\n", (fPackRowLengthSupport ? "YES": "NO"));
    r.appendf("Pack Flip Y support: %s\n", (fPackFlipYSupport ? "YES": "NO"));

    r.appendf("Texture Usage support: %s\n", (fTextureUsageSupport ? "YES": "NO"));
    r.appendf("Texture Storage support: %s\n", (fTexStorageSupport ? "YES": "NO"));
    r.appendf("GL_R support: %s\n", (fTextureRedSupport ? "YES": "NO"));
    r.appendf("GL_ARB_imaging support: %s\n", (fImagingSupport ? "YES": "NO"));
    r.appendf("Two Format Limit: %s\n", (fTwoFormatLimit ? "YES": "NO"));
    r.appendf("Fragment coord conventions support: %s\n",
             (fFragCoordsConventionSupport ? "YES": "NO"));
    r.appendf("Vertex array object support: %s\n", (fVertexArrayObjectSupport ? "YES": "NO"));
    r.appendf("Use non-VBO for dynamic data: %s\n",
             (fUseNonVBOVertexAndIndexDynamicData ? "YES" : "NO"));
    r.appendf("Full screen clear is free: %s\n", (fFullClearIsFree ? "YES" : "NO"));
    r.appendf("Drops tile on zero divide: %s\n", (fDropsTileOnZeroDivide ? "YES" : "NO"));
    return r;
}