/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include "src/core/SkAutoPixmapStorage.h" #include "src/core/SkMipmap.h" #include "src/core/SkUtils.h" #include "tools/flags/CommandLineFlags.h" #include "tools/fiddle/fiddle_main.h" static DEFINE_double(duration, 1.0, "The total duration, in seconds, of the animation we are drawing."); static DEFINE_double(frame, 1.0, "A double value in [0, 1] that specifies the point in animation to draw."); #include "include/gpu/GrBackendSurface.h" #include "src/gpu/GrContextPriv.h" #include "src/gpu/GrGpu.h" #include "src/gpu/GrRenderTarget.h" #include "tools/gpu/gl/GLTestContext.h" // Globals externed in fiddle_main.h sk_sp backingTexture; // not externed GrBackendTexture backEndTexture; sk_sp backingRenderTarget; // not externed GrBackendRenderTarget backEndRenderTarget; sk_sp backingTextureRenderTarget; // not externed GrBackendTexture backEndTextureRenderTarget; SkBitmap source; sk_sp image; double duration; // The total duration of the animation in seconds. double frame; // A value in [0, 1] of where we are in the animation. // Global used by the local impl of SkDebugf. std::ostringstream gTextOutput; // Global to record the GL driver info via create_direct_context(). std::ostringstream gGLDriverInfo; void SkDebugf(const char * fmt, ...) { va_list args; va_start(args, fmt); char formatbuffer[1024]; int n = vsnprintf(formatbuffer, sizeof(formatbuffer), fmt, args); va_end(args); if (n>=0 && n<=int(sizeof(formatbuffer))) { gTextOutput.write(formatbuffer, n); } } static void encode_to_base64(const void* data, size_t size, FILE* out) { const uint8_t* input = reinterpret_cast(data); const uint8_t* end = &input[size]; static const char codes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789+/"; while (input != end) { uint8_t b = (*input & 0xFC) >> 2; fputc(codes[b], out); b = (*input & 0x03) << 4; ++input; if (input == end) { fputc(codes[b], out); fputs("==", out); return; } b |= (*input & 0xF0) >> 4; fputc(codes[b], out); b = (*input & 0x0F) << 2; ++input; if (input == end) { fputc(codes[b], out); fputc('=', out); return; } b |= (*input & 0xC0) >> 6; fputc(codes[b], out); b = *input & 0x3F; fputc(codes[b], out); ++input; } } static void dump_output(const void* data, size_t size, const char* name, bool last = true) { printf("\t\"%s\": \"", name); encode_to_base64(data, size, stdout); fputs(last ? "\"\n" : "\",\n", stdout); } static void dump_output(const sk_sp& data, const char* name, bool last = true) { if (data) { dump_output(data->data(), data->size(), name, last); } } static sk_sp encode_snapshot(const sk_sp& surface) { sk_sp img(surface->makeImageSnapshot()); return img ? img->encodeToData() : nullptr; } static SkCanvas* prepare_canvas(SkCanvas * canvas) { canvas->clear(SK_ColorWHITE); return canvas; } #ifdef SK_GL static bool setup_backend_objects(GrContext* context, const SkBitmap& bm, const DrawOptions& options) { if (!context) { fputs("Context is null.\n", stderr); return false; } auto resourceProvider = context->priv().resourceProvider(); // This config must match the SkColorType used in draw.cpp in the SkImage and Surface factories auto format = resourceProvider->caps()->getDefaultBackendFormat( SkColorTypeToGrColorType(kRGBA_8888_SkColorType), GrRenderable::kNo); auto renderableFormat = resourceProvider->caps()->getDefaultBackendFormat( SkColorTypeToGrColorType(kRGBA_8888_SkColorType), GrRenderable::kYes); if (!bm.empty()) { SkPixmap originalPixmap; SkPixmap* pixmap = &originalPixmap; if (!bm.peekPixels(&originalPixmap)) { fputs("Unable to peekPixels.\n", stderr); return false; } SkAutoPixmapStorage rgbaPixmap; if (kN32_SkColorType != kRGBA_8888_SkColorType) { if (!rgbaPixmap.tryAlloc(bm.info().makeColorType(kRGBA_8888_SkColorType))) { fputs("Unable to alloc rgbaPixmap.\n", stderr); return false; } if (!bm.readPixels(rgbaPixmap)) { fputs("Unable to read rgbaPixmap.\n", stderr); return false; } pixmap = &rgbaPixmap; } int mipLevelCount = GrMipmapped::kYes == options.fMipMapping ? SkMipmap::ComputeLevelCount(bm.width(), bm.height()) : 1; std::unique_ptr texels(new GrMipLevel[mipLevelCount]); texels[0].fPixels = pixmap->addr(); texels[0].fRowBytes = pixmap->rowBytes(); for (int i = 1; i < mipLevelCount; i++) { texels[i].fPixels = nullptr; texels[i].fRowBytes = 0; } backingTexture = resourceProvider->createTexture( bm.dimensions(), format, GrColorType::kRGBA_8888, GrRenderable::kNo, 1, SkBudgeted::kNo, GrProtected::kNo, texels.get(), mipLevelCount); if (!backingTexture) { fputs("Failed to create backingTexture.\n", stderr); return false; } backEndTexture = backingTexture->getBackendTexture(); if (!backEndTexture.isValid()) { fputs("BackingTexture is invalid.\n", stderr); return false; } } SkISize offscreenDims = {options.fOffScreenWidth, options.fOffScreenHeight}; SkAutoTMalloc data(offscreenDims.area()); sk_memset32(data.get(), 0, offscreenDims.area()); { // This backend object should be renderable but not textureable. Given the limitations // of how we're creating it though it will wind up being secretly textureable. // We use this fact to initialize it with data but don't allow mipmaps GrMipLevel level0 = { data.get(), offscreenDims.width()*sizeof(uint32_t) }; sk_sp tmp = resourceProvider->createTexture( offscreenDims, renderableFormat, GrColorType::kRGBA_8888, GrRenderable::kYes, options.fOffScreenSampleCount, SkBudgeted::kNo, GrProtected::kNo, &level0, 1); if (!tmp || !tmp->asRenderTarget()) { fputs("GrTexture is invalid.\n", stderr); return false; } backingRenderTarget = sk_ref_sp(tmp->asRenderTarget()); backEndRenderTarget = backingRenderTarget->getBackendRenderTarget(); if (!backEndRenderTarget.isValid()) { fputs("BackEndRenderTarget is invalid.\n", stderr); return false; } } { int mipLevelCount = GrMipmapped::kYes == options.fOffScreenMipMapping ? SkMipmap::ComputeLevelCount(offscreenDims.width(), offscreenDims.height()) : 1; std::unique_ptr texels(new GrMipLevel[mipLevelCount]); texels[0].fPixels = data.get(); texels[0].fRowBytes = offscreenDims.width()*sizeof(uint32_t); for (int i = 1; i < mipLevelCount; i++) { texels[i].fPixels = nullptr; texels[i].fRowBytes = 0; } backingTextureRenderTarget = resourceProvider->createTexture( offscreenDims, renderableFormat, GrColorType::kRGBA_8888, GrRenderable::kYes, options.fOffScreenSampleCount, SkBudgeted::kNo, GrProtected::kNo, texels.get(), mipLevelCount); if (!backingTextureRenderTarget || !backingTextureRenderTarget->asRenderTarget()) { fputs("backingTextureRenderTarget is invalid.\n", stderr); return false; } backEndTextureRenderTarget = backingTextureRenderTarget->getBackendTexture(); if (!backEndTextureRenderTarget.isValid()) { fputs("backEndTextureRenderTarget is invalid.\n", stderr); return false; } } return true; } #endif int main(int argc, char** argv) { CommandLineFlags::Parse(argc, argv); duration = FLAGS_duration; frame = FLAGS_frame; DrawOptions options = GetDrawOptions(); // If textOnly then only do one type of image, otherwise the text // output is duplicated for each type. if (options.textOnly) { options.raster = true; options.gpu = false; options.pdf = false; options.skp = false; } if (options.source) { sk_sp data(SkData::MakeFromFileName(options.source)); if (!data) { perror(options.source); return 1; } else { image = SkImage::MakeFromEncoded(std::move(data)); if (!image) { perror("Unable to decode the source image."); return 1; } SkAssertResult(image->asLegacyBitmap(&source)); } } sk_sp rasterData, gpuData, pdfData, skpData; SkColorType colorType = kN32_SkColorType; sk_sp colorSpace = nullptr; if (options.f16) { SkASSERT(options.srgb); colorType = kRGBA_F16_SkColorType; colorSpace = SkColorSpace::MakeSRGBLinear(); } else if (options.srgb) { colorSpace = SkColorSpace::MakeSRGB(); } SkImageInfo info = SkImageInfo::Make(options.size.width(), options.size.height(), colorType, kPremul_SkAlphaType, colorSpace); if (options.raster) { auto rasterSurface = SkSurface::MakeRaster(info); srand(0); draw(prepare_canvas(rasterSurface->getCanvas())); rasterData = encode_snapshot(rasterSurface); } #ifdef SK_GL if (options.gpu) { std::unique_ptr glContext; sk_sp direct = create_direct_context(gGLDriverInfo, &glContext); if (!direct) { fputs("Unable to get GrContext.\n", stderr); } else { if (!setup_backend_objects(direct.get(), source, options)) { fputs("Unable to create backend objects.\n", stderr); exit(1); } auto surface = SkSurface::MakeRenderTarget(direct.get(), SkBudgeted::kNo, info); if (!surface) { fputs("Unable to get render surface.\n", stderr); exit(1); } srand(0); draw(prepare_canvas(surface->getCanvas())); gpuData = encode_snapshot(surface); } } #endif if (options.pdf) { SkDynamicMemoryWStream pdfStream; auto document = SkPDF::MakeDocument(&pdfStream); if (document) { srand(0); draw(prepare_canvas(document->beginPage(options.size.width(), options.size.height()))); document->close(); pdfData = pdfStream.detachAsData(); } } if (options.skp) { auto size = SkSize::Make(options.size); SkPictureRecorder recorder; srand(0); draw(prepare_canvas(recorder.beginRecording(size.width(), size.height()))); auto picture = recorder.finishRecordingAsPicture(); SkDynamicMemoryWStream skpStream; picture->serialize(&skpStream); skpData = skpStream.detachAsData(); } printf("{\n"); if (!options.textOnly) { dump_output(rasterData, "Raster", false); dump_output(gpuData, "Gpu", false); dump_output(pdfData, "Pdf", false); dump_output(skpData, "Skp", false); } else { std::string textoutput = gTextOutput.str(); dump_output(textoutput.c_str(), textoutput.length(), "Text", false); } std::string glinfo = gGLDriverInfo.str(); dump_output(glinfo.c_str(), glinfo.length(), "GLInfo", true); printf("}\n"); return 0; }