void SkSVGDevice::AutoElement::addTextAttributes(const SkPaint& paint) { this->addAttribute("font-size", paint.getTextSize()); if (const char* textAlign = svg_text_align(paint.getTextAlign())) { this->addAttribute("text-anchor", textAlign); } SkString familyName; SkTHashSet<SkString> familySet; SkAutoTUnref<const SkTypeface> tface(paint.getTypeface() ? SkRef(paint.getTypeface()) : SkTypeface::RefDefault()); SkASSERT(tface); SkTypeface::Style style = tface->style(); if (style & SkTypeface::kItalic) { this->addAttribute("font-style", "italic"); } if (style & SkTypeface::kBold) { this->addAttribute("font-weight", "bold"); } SkAutoTUnref<SkTypeface::LocalizedStrings> familyNameIter(tface->createFamilyNameIterator()); SkTypeface::LocalizedString familyString; while (familyNameIter->next(&familyString)) { if (familySet.contains(familyString.fString)) { continue; } familySet.add(familyString.fString); familyName.appendf((familyName.isEmpty() ? "%s" : ", %s"), familyString.fString.c_str()); } if (!familyName.isEmpty()) { this->addAttribute("font-family", familyName); } }
int main(int argc, char**argv) { #if 0 // TODO: use Mac main loop Display* display = XOpenDisplay(nullptr); Application* app = Application::Create(argc, argv, (void*)display); // Get the file descriptor for the X display int x11_fd = ConnectionNumber(display); fd_set in_fds; SkTHashSet<sk_app::Window_mac*> pendingWindows; bool done = false; while (!done) { // Create a file description set containing x11_fd FD_ZERO(&in_fds); FD_SET(x11_fd, &in_fds); // Set a sleep timer struct timeval tv; tv.tv_usec = 100; tv.tv_sec = 0; // Wait for an event on the file descriptor or for timer expiration (void) select(1, &in_fds, NULL, NULL, &tv); // Handle XEvents (if any) and flush the input XEvent event; while (XPending(display) && !done) { XNextEvent(display, &event); sk_app::Window_mac* win = sk_app::Window_mac::gWindowMap.find(event.xany.window); // paint and resize events get collapsed switch (event.type) { case Expose: win->markPendingPaint(); pendingWindows.add(win); break; case ConfigureNotify: win->markPendingResize(event.xconfigurerequest.width, event.xconfigurerequest.height); pendingWindows.add(win); break; default: if (win->handleEvent(event)) { done = true; } break; } } } delete app; XCloseDisplay(display); #endif return 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) { // FIXME: This code is currently unreachable because we create an empty generator when // we fail to create a codec. SkDebugf("Codec could not be created for %s\n", skpName.c_str()); gSkpToUnknownCount[skpName]++; 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::kDNG_SkEncodedFormat: ext = "dng"; break; case SkEncodedFormat::kWBMP_SkEncodedFormat: ext = "wbmp"; break; case SkEncodedFormat::kWEBP_SkEncodedFormat: ext = "webp"; break; default: // This should be unreachable because we cannot create a codec if we do not know // the image type. SkASSERT(false); } if (FLAGS_testDecode) { SkBitmap bitmap; SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); bitmap.allocPixels(info); if (SkCodec::kSuccess != codec->getPixels(info, bitmap.getPixels(), bitmap.rowBytes())) { SkDebugf("Decoding failed for %s\n", skpName.c_str()); gSkpToUnknownCount[skpName]++; return; } } if (FLAGS_writeImages) { 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()); } gKnown++; }
static void gather_uninteresting_hashes() { if (!FLAGS_uninterestingHashesFile.isEmpty()) { SkAutoTUnref<SkData> data(SkData::NewFromFileName(FLAGS_uninterestingHashesFile[0])); if (!data) { SkDebugf("WARNING: unable to read uninteresting hashes from %s\n", FLAGS_uninterestingHashesFile[0]); return; } SkTArray<SkString> hashes; SkStrSplit((const char*)data->data(), "\n", &hashes); for (const SkString& hash : hashes) { gUninterestingHashes.add(hash); } SkDebugf("FYI: loaded %d distinct uninteresting hashes from %d lines\n", gUninterestingHashes.count(), hashes.count()); } }
DEF_TEST(HashSetCopyCounter, r) { SkTHashSet<CopyCounter, hash_copy_counter> set; uint32_t globalCounter = 0; CopyCounter copyCounter1(1, &globalCounter); CopyCounter copyCounter2(2, &globalCounter); REPORTER_ASSERT(r, globalCounter == 0); set.add(copyCounter1); REPORTER_ASSERT(r, globalCounter == 1); REPORTER_ASSERT(r, set.contains(copyCounter1)); REPORTER_ASSERT(r, globalCounter == 1); set.add(copyCounter1); // We allow copies for same-value adds for now. REPORTER_ASSERT(r, globalCounter == 2); set.add(copyCounter2); REPORTER_ASSERT(r, globalCounter == 3); REPORTER_ASSERT(r, set.contains(copyCounter1)); REPORTER_ASSERT(r, set.contains(copyCounter2)); REPORTER_ASSERT(r, globalCounter == 3); set.add(copyCounter1); set.add(copyCounter2); // We allow copies for same-value adds for now. REPORTER_ASSERT(r, globalCounter == 5); }
static void gather_uninteresting_hashes() { if (!FLAGS_uninterestingHashesFile.isEmpty()) { SkAutoTUnref<SkData> data(SkData::NewFromFileName(FLAGS_uninterestingHashesFile[0])); SkTArray<SkString> hashes; SkStrSplit((const char*)data->data(), "\n", &hashes); for (const SkString& hash : hashes) { gUninterestingHashes.add(hash); } } }
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()); }
static void WriteToDisk(const Task& task, SkString md5, const char* ext, SkStream* data, size_t len, const SkBitmap* bitmap) { JsonWriter::BitmapResult result; result.name = task.src->name(); result.config = task.sink.tag; result.sourceType = task.src.tag; result.sourceOptions = task.src.options; result.ext = ext; result.md5 = md5; JsonWriter::AddBitmapResult(result); // If an MD5 is uninteresting, we want it noted in the JSON file, // but don't want to dump it out as a .png (or whatever ext is). if (gUninterestingHashes.contains(md5)) { return; } const char* dir = FLAGS_writePath[0]; if (0 == strcmp(dir, "@")) { // Needed for iOS. dir = FLAGS_resourcePath[0]; } sk_mkdir(dir); SkString path; if (FLAGS_nameByHash) { path = SkOSPath::Join(dir, result.md5.c_str()); path.append("."); path.append(ext); if (sk_exists(path.c_str())) { return; // Content-addressed. If it exists already, we're done. } } else { path = SkOSPath::Join(dir, task.sink.tag); sk_mkdir(path.c_str()); path = SkOSPath::Join(path.c_str(), task.src.tag); sk_mkdir(path.c_str()); if (strcmp(task.src.options, "") != 0) { path = SkOSPath::Join(path.c_str(), task.src.options); sk_mkdir(path.c_str()); } path = SkOSPath::Join(path.c_str(), task.src->name().c_str()); path.append("."); path.append(ext); } if (bitmap) { if (!dump_png(*bitmap, path.c_str(), result.md5.c_str())) { fail(SkStringPrintf("Can't encode PNG to %s.\n", path.c_str())); return; } } else { SkFILEWStream file(path.c_str()); if (!file.isValid()) { fail(SkStringPrintf("Can't open %s for writing.\n", path.c_str())); return; } if (!file.writeStream(data, len)) { fail(SkStringPrintf("Can't write to %s.\n", path.c_str())); return; } } }
static void Run(Task* task) { SkString name = task->src->name(); // We'll skip drawing this Src/Sink pair if: // - the Src vetoes the Sink; // - this Src / Sink combination is on the blacklist; // - it's a dry run. SkString note(task->src->veto(task->sink->flags()) ? " (veto)" : ""); 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 && note.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(), nullptr); SkASSERT(bitmap.drawsNothing()); } else if (!bitmap.drawsNothing()) { WriteToDisk(*task, md5, ext, nullptr, 0, &bitmap); } } } timer.end(); done(timer.fWall, task->sink.tag, task->src.tag, task->src.options, name, note, log); }
static void add_gold(JsonWriter::BitmapResult r) { gGold.add(Gold(r.config, r.sourceType, r.sourceOptions, r.name, r.md5)); }
DEF_TEST(HashSet, r) { SkTHashSet<SkString> set; set.add(SkString("Hello")); set.add(SkString("World")); REPORTER_ASSERT(r, set.count() == 2); REPORTER_ASSERT(r, set.contains(SkString("Hello"))); REPORTER_ASSERT(r, set.contains(SkString("World"))); REPORTER_ASSERT(r, !set.contains(SkString("Goodbye"))); REPORTER_ASSERT(r, set.find(SkString("Hello"))); REPORTER_ASSERT(r, *set.find(SkString("Hello")) == SkString("Hello")); set.remove(SkString("Hello")); REPORTER_ASSERT(r, !set.contains(SkString("Hello"))); REPORTER_ASSERT(r, set.count() == 1); set.reset(); REPORTER_ASSERT(r, set.count() == 0); }
static void Run(const Task& task) { SkString name = task.src->name(); SkString log; if (!FLAGS_dryRun) { SkBitmap bitmap; SkDynamicMemoryWStream stream; start(task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str()); Error err = task.sink->draw(*task.src, &bitmap, &stream, &log); if (!log.isEmpty()) { info("%s %s %s %s:\n%s\n", task.sink.tag.c_str() , task.src.tag.c_str() , task.src.options.c_str() , name.c_str() , log.c_str()); } if (!err.isEmpty()) { if (err.isFatal()) { fail(SkStringPrintf("%s %s %s %s: %s", task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str(), err.c_str())); } else { done(task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str()); return; } } // We're likely switching threads here, so we must capture by value, [=] or [foo,bar]. SkStreamAsset* data = stream.detachAsStream(); gDefinitelyThreadSafeWork.add([task,name,bitmap,data]{ SkAutoTDelete<SkStreamAsset> ownedData(data); // Why doesn't the copy constructor do this when we have pre-locked pixels? bitmap.lockPixels(); 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.c_str(), task.src.tag.c_str(), task.src.options.c_str(), 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(), nullptr); SkASSERT(bitmap.drawsNothing()); } else if (!bitmap.drawsNothing()) { WriteToDisk(task, md5, ext, nullptr, 0, &bitmap); } } }); } done(task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str()); }
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; 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 { 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); }
int main(int argc, char**argv) { Display* display = XOpenDisplay(nullptr); Application* app = Application::Create(argc, argv, (void*)display); // Get the file descriptor for the X display int x11_fd = ConnectionNumber(display); int count = x11_fd + 1; SkTHashSet<sk_app::Window_unix*> pendingWindows; bool done = false; while (!done) { // Create a file description set containing x11_fd fd_set in_fds; FD_ZERO(&in_fds); FD_SET(x11_fd, &in_fds); // Set a sleep timer struct timeval tv; tv.tv_usec = 100; tv.tv_sec = 0; while (!XPending(display)) { // Wait for an event on the file descriptor or for timer expiration (void) select(count, &in_fds, NULL, NULL, &tv); } // Handle XEvents (if any) and flush the input int count = XPending(display); while (count-- && !done) { XEvent event; XNextEvent(display, &event); sk_app::Window_unix* win = sk_app::Window_unix::gWindowMap.find(event.xany.window); // paint and resize events get collapsed switch (event.type) { case Expose: win->markPendingPaint(); pendingWindows.add(win); break; case ConfigureNotify: win->markPendingResize(event.xconfigurerequest.width, event.xconfigurerequest.height); pendingWindows.add(win); break; default: if (win->handleEvent(event)) { done = true; } break; } } pendingWindows.foreach(finishWindow); if (pendingWindows.count() > 0) { app->onIdle(); } pendingWindows.reset(); XFlush(display); } delete app; XCloseDisplay(display); return 0; }