2015-12-10 15:52:45 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2015 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2017-02-15 15:20:30 +00:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <sstream>
|
2017-02-13 16:39:43 +00:00
|
|
|
#include <string>
|
2015-12-10 15:52:45 +00:00
|
|
|
|
2017-12-12 21:24:41 +00:00
|
|
|
#include "SkAutoPixmapStorage.h"
|
2017-04-19 18:05:14 +00:00
|
|
|
#include "SkCommandLineFlags.h"
|
2017-11-17 19:59:43 +00:00
|
|
|
#include "SkMipMap.h"
|
2017-11-16 19:59:48 +00:00
|
|
|
#include "SkUtils.h"
|
2017-04-19 18:05:14 +00:00
|
|
|
|
2015-12-10 15:52:45 +00:00
|
|
|
#include "fiddle_main.h"
|
|
|
|
|
2017-04-19 18:05:14 +00:00
|
|
|
DEFINE_double(duration, 1.0, "The total duration, in seconds, of the animation we are drawing.");
|
|
|
|
DEFINE_double(frame, 1.0, "A double value in [0, 1] that specifies the point in animation to draw.");
|
|
|
|
|
2017-11-16 19:59:48 +00:00
|
|
|
#include "GrBackendSurface.h"
|
|
|
|
#include "GrContextPriv.h"
|
|
|
|
#include "GrGpu.h"
|
|
|
|
|
|
|
|
#include "GrTest.h"
|
|
|
|
|
|
|
|
|
2015-12-10 15:52:45 +00:00
|
|
|
// Globals externed in fiddle_main.h
|
2017-11-16 19:59:48 +00:00
|
|
|
sk_sp<GrTexture> backingTexture; // not externed
|
|
|
|
GrBackendTexture backEndTexture;
|
|
|
|
|
|
|
|
sk_sp<GrRenderTarget> backingRenderTarget; // not externed
|
|
|
|
GrBackendRenderTarget backEndRenderTarget;
|
|
|
|
|
|
|
|
sk_sp<GrTexture> backingTextureRenderTarget; // not externed
|
|
|
|
GrBackendTexture backEndTextureRenderTarget;
|
|
|
|
|
2015-12-10 15:52:45 +00:00
|
|
|
SkBitmap source;
|
2016-04-18 15:17:56 +00:00
|
|
|
sk_sp<SkImage> image;
|
2017-04-19 18:05:14 +00:00
|
|
|
double duration; // The total duration of the animation in seconds.
|
|
|
|
double frame; // A value in [0, 1] of where we are in the animation.
|
2015-12-10 15:52:45 +00:00
|
|
|
|
2017-02-15 15:20:30 +00:00
|
|
|
// Global used by the local impl of SkDebugf.
|
|
|
|
std::ostringstream gTextOutput;
|
2017-02-13 16:39:43 +00:00
|
|
|
|
2017-06-01 17:24:11 +00:00
|
|
|
// Global to record the GL driver info via create_grcontext().
|
|
|
|
std::ostringstream gGLDriverInfo;
|
|
|
|
|
2017-02-13 16:39:43 +00:00
|
|
|
void SkDebugf(const char * fmt, ...) {
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
2017-02-15 15:20:30 +00:00
|
|
|
char formatbuffer[1024];
|
|
|
|
int n = vsnprintf(formatbuffer, sizeof(formatbuffer), fmt, args);
|
2017-02-13 16:39:43 +00:00
|
|
|
va_end(args);
|
|
|
|
if (n>=0 && n<=int(sizeof(formatbuffer))) {
|
2017-02-15 15:20:30 +00:00
|
|
|
gTextOutput.write(formatbuffer, n);
|
2017-02-13 16:39:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-10 15:52:45 +00:00
|
|
|
static void encode_to_base64(const void* data, size_t size, FILE* out) {
|
|
|
|
const uint8_t* input = reinterpret_cast<const uint8_t*>(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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-15 15:20:30 +00:00
|
|
|
static void dump_output(const void* data, size_t size,
|
2017-02-13 16:39:43 +00:00
|
|
|
const char* name, bool last = true) {
|
|
|
|
printf("\t\"%s\": \"", name);
|
2017-02-15 15:20:30 +00:00
|
|
|
encode_to_base64(data, size, stdout);
|
2017-02-13 16:39:43 +00:00
|
|
|
fputs(last ? "\"\n" : "\",\n", stdout);
|
|
|
|
}
|
|
|
|
|
2017-02-15 15:20:30 +00:00
|
|
|
static void dump_output(const sk_sp<SkData>& data,
|
|
|
|
const char* name, bool last = true) {
|
|
|
|
if (data) {
|
|
|
|
dump_output(data->data(), data->size(), name, last);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-11 20:03:13 +00:00
|
|
|
static sk_sp<SkData> encode_snapshot(const sk_sp<SkSurface>& surface) {
|
2016-04-18 15:17:56 +00:00
|
|
|
sk_sp<SkImage> img(surface->makeImageSnapshot());
|
2017-07-11 20:03:13 +00:00
|
|
|
return img ? img->encodeToData() : nullptr;
|
2015-12-10 15:52:45 +00:00
|
|
|
}
|
|
|
|
|
2017-06-14 19:54:32 +00:00
|
|
|
static SkCanvas* prepare_canvas(SkCanvas * canvas) {
|
|
|
|
canvas->clear(SK_ColorWHITE);
|
|
|
|
return canvas;
|
|
|
|
}
|
|
|
|
|
2017-11-17 19:59:43 +00:00
|
|
|
static bool setup_backend_objects(GrContext* context,
|
|
|
|
const SkBitmap& bm,
|
|
|
|
const DrawOptions& options) {
|
2017-11-16 19:59:48 +00:00
|
|
|
if (!context) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-16 20:07:54 +00:00
|
|
|
auto resourceProvider = context->contextPriv().resourceProvider();
|
|
|
|
|
2017-11-16 19:59:48 +00:00
|
|
|
GrSurfaceDesc backingDesc;
|
|
|
|
backingDesc.fFlags = kNone_GrSurfaceFlags;
|
|
|
|
backingDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
|
|
|
|
backingDesc.fWidth = bm.width();
|
|
|
|
backingDesc.fHeight = bm.height();
|
2017-12-19 18:15:02 +00:00
|
|
|
// This config must match the SkColorType used in draw.cpp in the SkImage and Surface factories
|
2017-12-13 20:00:45 +00:00
|
|
|
backingDesc.fConfig = kRGBA_8888_GrPixelConfig;
|
2018-02-03 00:25:12 +00:00
|
|
|
backingDesc.fSampleCnt = 0;
|
2017-11-16 19:59:48 +00:00
|
|
|
|
|
|
|
if (!bm.empty()) {
|
2017-12-12 21:24:41 +00:00
|
|
|
SkPixmap originalPixmap;
|
|
|
|
SkPixmap* pixmap = &originalPixmap;
|
|
|
|
if (!bm.peekPixels(&originalPixmap)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkAutoPixmapStorage rgbaPixmap;
|
|
|
|
if (kN32_SkColorType != kRGBA_8888_SkColorType) {
|
|
|
|
if (!rgbaPixmap.tryAlloc(bm.info().makeColorType(kRGBA_8888_SkColorType))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!bm.readPixels(rgbaPixmap)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
pixmap = &rgbaPixmap;
|
|
|
|
}
|
2017-11-17 19:59:43 +00:00
|
|
|
int mipLevelCount = GrMipMapped::kYes == options.fMipMapping
|
|
|
|
? SkMipMap::ComputeLevelCount(bm.width(), bm.height())
|
|
|
|
: 1;
|
|
|
|
std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
|
|
|
|
|
2017-12-12 21:24:41 +00:00
|
|
|
texels[0].fPixels = pixmap->addr();
|
|
|
|
texels[0].fRowBytes = pixmap->rowBytes();
|
2017-11-17 19:59:43 +00:00
|
|
|
|
|
|
|
for (int i = 1; i < mipLevelCount; i++) {
|
|
|
|
texels[i].fPixels = nullptr;
|
|
|
|
texels[i].fRowBytes = 0;
|
|
|
|
}
|
2017-11-16 19:59:48 +00:00
|
|
|
|
2018-01-16 20:07:54 +00:00
|
|
|
backingTexture = resourceProvider->createTexture(backingDesc, SkBudgeted::kNo,
|
|
|
|
texels.get(), mipLevelCount,
|
|
|
|
SkDestinationSurfaceColorMode::kLegacy);
|
2017-11-16 19:59:48 +00:00
|
|
|
if (!backingTexture) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-12-13 20:00:45 +00:00
|
|
|
backEndTexture = backingTexture->getBackendTexture();
|
2017-11-16 19:59:48 +00:00
|
|
|
if (!backEndTexture.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
backingDesc.fFlags = kRenderTarget_GrSurfaceFlag;
|
2017-11-17 19:59:43 +00:00
|
|
|
backingDesc.fWidth = options.fOffScreenWidth;
|
|
|
|
backingDesc.fHeight = options.fOffScreenHeight;
|
|
|
|
backingDesc.fSampleCnt = options.fOffScreenSampleCount;
|
2017-11-16 19:59:48 +00:00
|
|
|
|
2017-11-17 19:59:43 +00:00
|
|
|
SkAutoTMalloc<uint32_t> data(backingDesc.fWidth * backingDesc.fHeight);
|
|
|
|
sk_memset32(data.get(), 0, backingDesc.fWidth * backingDesc.fHeight);
|
2017-11-16 19:59:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
{
|
2017-11-17 19:59:43 +00:00
|
|
|
// 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(), backingDesc.fWidth*sizeof(uint32_t) };
|
|
|
|
|
2018-01-16 20:07:54 +00:00
|
|
|
sk_sp<GrTexture> tmp = resourceProvider->createTexture(
|
2017-11-16 19:59:48 +00:00
|
|
|
backingDesc, SkBudgeted::kNo,
|
|
|
|
&level0, 1,
|
|
|
|
SkDestinationSurfaceColorMode::kLegacy);
|
|
|
|
if (!tmp || !tmp->asRenderTarget()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
backingRenderTarget = sk_ref_sp(tmp->asRenderTarget());
|
|
|
|
|
2017-12-13 20:00:45 +00:00
|
|
|
backEndRenderTarget = backingRenderTarget->getBackendRenderTarget();
|
2017-11-16 19:59:48 +00:00
|
|
|
if (!backEndRenderTarget.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2017-11-17 19:59:43 +00:00
|
|
|
int mipLevelCount = GrMipMapped::kYes == options.fOffScreenMipMapping
|
|
|
|
? SkMipMap::ComputeLevelCount(backingDesc.fWidth, backingDesc.fHeight)
|
|
|
|
: 1;
|
|
|
|
std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
|
|
|
|
|
|
|
|
texels[0].fPixels = data.get();
|
|
|
|
texels[0].fRowBytes = backingDesc.fWidth*sizeof(uint32_t);
|
|
|
|
|
|
|
|
for (int i = 1; i < mipLevelCount; i++) {
|
|
|
|
texels[i].fPixels = nullptr;
|
|
|
|
texels[i].fRowBytes = 0;
|
|
|
|
}
|
|
|
|
|
2018-01-16 20:07:54 +00:00
|
|
|
backingTextureRenderTarget = resourceProvider->createTexture(
|
2017-11-16 19:59:48 +00:00
|
|
|
backingDesc, SkBudgeted::kNo,
|
2017-11-17 19:59:43 +00:00
|
|
|
texels.get(), mipLevelCount,
|
2017-11-16 19:59:48 +00:00
|
|
|
SkDestinationSurfaceColorMode::kLegacy);
|
|
|
|
if (!backingTextureRenderTarget || !backingTextureRenderTarget->asRenderTarget()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-12-13 20:00:45 +00:00
|
|
|
backEndTextureRenderTarget = backingTextureRenderTarget->getBackendTexture();
|
2017-11-16 19:59:48 +00:00
|
|
|
if (!backEndTextureRenderTarget.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-19 18:05:14 +00:00
|
|
|
int main(int argc, char** argv) {
|
|
|
|
SkCommandLineFlags::Parse(argc, argv);
|
|
|
|
duration = FLAGS_duration;
|
|
|
|
frame = FLAGS_frame;
|
2017-02-13 16:39:43 +00:00
|
|
|
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;
|
|
|
|
}
|
2015-12-10 15:52:45 +00:00
|
|
|
if (options.source) {
|
2016-08-02 21:40:46 +00:00
|
|
|
sk_sp<SkData> data(SkData::MakeFromFileName(options.source));
|
2015-12-10 15:52:45 +00:00
|
|
|
if (!data) {
|
|
|
|
perror(options.source);
|
|
|
|
return 1;
|
|
|
|
} else {
|
2016-04-18 15:17:56 +00:00
|
|
|
image = SkImage::MakeFromEncoded(std::move(data));
|
2015-12-10 15:52:45 +00:00
|
|
|
if (!image) {
|
|
|
|
perror("Unable to decode the source image.");
|
|
|
|
return 1;
|
|
|
|
}
|
2017-11-16 19:59:48 +00:00
|
|
|
SkAssertResult(image->asLegacyBitmap(&source, SkImage::kRO_LegacyBitmapMode));
|
2015-12-10 15:52:45 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-25 18:29:34 +00:00
|
|
|
sk_sp<SkData> rasterData, gpuData, pdfData, skpData;
|
2016-11-29 22:14:58 +00:00
|
|
|
SkColorType colorType = kN32_SkColorType;
|
|
|
|
sk_sp<SkColorSpace> colorSpace = nullptr;
|
|
|
|
if (options.f16) {
|
|
|
|
SkASSERT(options.srgb);
|
|
|
|
colorType = kRGBA_F16_SkColorType;
|
2017-02-07 18:56:11 +00:00
|
|
|
colorSpace = SkColorSpace::MakeSRGBLinear();
|
2016-11-29 22:14:58 +00:00
|
|
|
} else if (options.srgb) {
|
2017-02-07 18:56:11 +00:00
|
|
|
colorSpace = SkColorSpace::MakeSRGB();
|
2016-11-29 22:14:58 +00:00
|
|
|
}
|
|
|
|
SkImageInfo info = SkImageInfo::Make(options.size.width(), options.size.height(), colorType,
|
|
|
|
kPremul_SkAlphaType, colorSpace);
|
2015-12-10 15:52:45 +00:00
|
|
|
if (options.raster) {
|
2016-11-29 22:14:58 +00:00
|
|
|
auto rasterSurface = SkSurface::MakeRaster(info);
|
2016-05-23 16:02:38 +00:00
|
|
|
srand(0);
|
2017-06-14 19:54:32 +00:00
|
|
|
draw(prepare_canvas(rasterSurface->getCanvas()));
|
2017-07-11 20:03:13 +00:00
|
|
|
rasterData = encode_snapshot(rasterSurface);
|
2015-12-10 15:52:45 +00:00
|
|
|
}
|
|
|
|
if (options.gpu) {
|
2017-11-16 19:59:48 +00:00
|
|
|
sk_sp<GrContext> grContext = create_grcontext(gGLDriverInfo);
|
2015-12-10 15:52:45 +00:00
|
|
|
if (!grContext) {
|
2016-07-27 18:17:18 +00:00
|
|
|
fputs("Unable to get GrContext.\n", stderr);
|
2015-12-10 15:52:45 +00:00
|
|
|
} else {
|
2017-11-17 19:59:43 +00:00
|
|
|
if (!setup_backend_objects(grContext.get(), source, options)) {
|
2017-11-16 19:59:48 +00:00
|
|
|
fputs("Unable to create backend objects.\n", stderr);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2016-11-29 22:14:58 +00:00
|
|
|
auto surface = SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kNo, info);
|
2015-12-10 15:52:45 +00:00
|
|
|
if (!surface) {
|
|
|
|
fputs("Unable to get render surface.\n", stderr);
|
|
|
|
exit(1);
|
|
|
|
}
|
2016-05-23 16:02:38 +00:00
|
|
|
srand(0);
|
2017-06-14 19:54:32 +00:00
|
|
|
draw(prepare_canvas(surface->getCanvas()));
|
2017-07-11 20:03:13 +00:00
|
|
|
gpuData = encode_snapshot(surface);
|
2015-12-10 15:52:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (options.pdf) {
|
|
|
|
SkDynamicMemoryWStream pdfStream;
|
2016-05-03 19:10:04 +00:00
|
|
|
sk_sp<SkDocument> document(SkDocument::MakePDF(&pdfStream));
|
2016-08-31 11:58:19 +00:00
|
|
|
if (document) {
|
|
|
|
srand(0);
|
2017-06-14 19:54:32 +00:00
|
|
|
draw(prepare_canvas(document->beginPage(options.size.width(), options.size.height())));
|
2016-08-31 11:58:19 +00:00
|
|
|
document->close();
|
2016-09-12 19:01:44 +00:00
|
|
|
pdfData = pdfStream.detachAsData();
|
2016-08-31 11:58:19 +00:00
|
|
|
}
|
2015-12-10 15:52:45 +00:00
|
|
|
}
|
|
|
|
if (options.skp) {
|
|
|
|
SkSize size;
|
|
|
|
size = options.size;
|
|
|
|
SkPictureRecorder recorder;
|
2016-05-23 16:02:38 +00:00
|
|
|
srand(0);
|
2017-06-14 19:54:32 +00:00
|
|
|
draw(prepare_canvas(recorder.beginRecording(size.width(), size.height())));
|
2016-03-25 18:29:34 +00:00
|
|
|
auto picture = recorder.finishRecordingAsPicture();
|
2015-12-10 15:52:45 +00:00
|
|
|
SkDynamicMemoryWStream skpStream;
|
|
|
|
picture->serialize(&skpStream);
|
2016-09-12 19:01:44 +00:00
|
|
|
skpData = skpStream.detachAsData();
|
2015-12-10 15:52:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
printf("{\n");
|
2017-02-13 18:55:17 +00:00
|
|
|
if (!options.textOnly) {
|
2017-06-01 17:24:11 +00:00
|
|
|
dump_output(rasterData, "Raster", false);
|
|
|
|
dump_output(gpuData, "Gpu", false);
|
|
|
|
dump_output(pdfData, "Pdf", false);
|
|
|
|
dump_output(skpData, "Skp", false);
|
2017-02-13 18:55:17 +00:00
|
|
|
} else {
|
2017-02-15 15:20:30 +00:00
|
|
|
std::string textoutput = gTextOutput.str();
|
2017-06-01 17:24:11 +00:00
|
|
|
dump_output(textoutput.c_str(), textoutput.length(), "Text", false);
|
2017-02-13 18:55:17 +00:00
|
|
|
}
|
2017-06-01 17:24:11 +00:00
|
|
|
std::string glinfo = gGLDriverInfo.str();
|
|
|
|
dump_output(glinfo.c_str(), glinfo.length(), "GLInfo", true);
|
2015-12-10 15:52:45 +00:00
|
|
|
printf("}\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|