bool SkPdfNativeObject::applyFlateDecodeFilter() { if (!SkFlate::HaveFlate()) { SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kNoFlateLibrary_SkPdfIssue, "forgot to link with flate library?", NULL, NULL); return false; } const unsigned char* old = fStr.fBuffer; bool deleteOld = isStreamOwned(); SkMemoryStream skstream(fStr.fBuffer, fStr.fBytes >> 2, false); SkDynamicMemoryWStream uncompressedData; if (SkFlate::Inflate(&skstream, &uncompressedData)) { fStr.fBytes = (uncompressedData.bytesWritten() << 2) + kOwnedStreamBit + kUnfilteredStreamBit; fStr.fBuffer = (const unsigned char*)new unsigned char[uncompressedData.bytesWritten()]; uncompressedData.copyTo((void*)fStr.fBuffer); if (deleteOld) { delete[] old; } return true; } else { SkPdfReport(kIgnoreError_SkPdfIssueSeverity, kBadStream_SkPdfIssue, "inflate failed", this, NULL); return false; } }
static void TestWStream(skiatest::Reporter* reporter) { SkDynamicMemoryWStream ds; const char s[] = "abcdefghijklmnopqrstuvwxyz"; int i; for (i = 0; i < 100; i++) { REPORTER_ASSERT(reporter, ds.write(s, 26)); } REPORTER_ASSERT(reporter, ds.bytesWritten() == 100 * 26); char* dst = new char[100 * 26 + 1]; dst[100*26] = '*'; ds.copyTo(dst); REPORTER_ASSERT(reporter, dst[100*26] == '*'); for (i = 0; i < 100; i++) { REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0); } { std::unique_ptr<SkStreamAsset> stream(ds.detachAsStream()); REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength()); REPORTER_ASSERT(reporter, ds.bytesWritten() == 0); test_loop_stream(reporter, stream.get(), s, 26, 100); std::unique_ptr<SkStreamAsset> stream2(stream->duplicate()); test_loop_stream(reporter, stream2.get(), s, 26, 100); std::unique_ptr<SkStreamAsset> stream3(stream->fork()); REPORTER_ASSERT(reporter, stream3->isAtEnd()); char tmp; size_t bytes = stream->read(&tmp, 1); REPORTER_ASSERT(reporter, 0 == bytes); stream3->rewind(); test_loop_stream(reporter, stream3.get(), s, 26, 100); } for (i = 0; i < 100; i++) { REPORTER_ASSERT(reporter, ds.write(s, 26)); } REPORTER_ASSERT(reporter, ds.bytesWritten() == 100 * 26); { // Test that this works after a snapshot. std::unique_ptr<SkStreamAsset> stream(ds.detachAsStream()); REPORTER_ASSERT(reporter, ds.bytesWritten() == 0); test_loop_stream(reporter, stream.get(), s, 26, 100); std::unique_ptr<SkStreamAsset> stream2(stream->duplicate()); test_loop_stream(reporter, stream2.get(), s, 26, 100); } delete[] dst; SkString tmpDir = skiatest::GetTmpDir(); if (!tmpDir.isEmpty()) { test_filestreams(reporter, tmpDir.c_str()); } }
bool DrawingDisplayItem::equals(const DisplayItem& other) const { if (!DisplayItem::equals(other)) return false; RefPtr<const SkPicture> picture = this->picture(); RefPtr<const SkPicture> otherPicture = static_cast<const DrawingDisplayItem&>(other).picture(); if (!picture && !otherPicture) return true; if (!picture || !otherPicture) return false; switch (getUnderInvalidationCheckingMode()) { case DrawingDisplayItem::CheckPicture: { if (picture->approximateOpCount() != otherPicture->approximateOpCount()) return false; SkDynamicMemoryWStream pictureSerialized; picture->serialize(&pictureSerialized); SkDynamicMemoryWStream otherPictureSerialized; otherPicture->serialize(&otherPictureSerialized); if (pictureSerialized.bytesWritten() != otherPictureSerialized.bytesWritten()) return false; RefPtr<SkData> oldData = adoptRef(otherPictureSerialized.copyToData()); RefPtr<SkData> newData = adoptRef(pictureSerialized.copyToData()); return oldData->equals(newData.get()); } case DrawingDisplayItem::CheckBitmap: { SkRect rect = picture->cullRect(); if (rect != otherPicture->cullRect()) return false; SkBitmap bitmap; bitmap.allocPixels(SkImageInfo::MakeN32Premul(rect.width(), rect.height())); SkCanvas canvas(bitmap); canvas.translate(-rect.x(), -rect.y()); canvas.drawPicture(otherPicture.get()); SkPaint diffPaint; diffPaint.setXfermodeMode(SkXfermode::kDifference_Mode); canvas.drawPicture(picture.get(), nullptr, &diffPaint); return bitmapIsAllZero(bitmap); } default: ASSERT_NOT_REACHED(); } return false; }
DEF_TEST(StreamPeek_BlockMemoryStream, rep) { const static int kSeed = 1234; SkRandom valueSource(kSeed); SkRandom rand(kSeed << 1); uint8_t buffer[4096]; SkDynamicMemoryWStream dynamicMemoryWStream; size_t totalWritten = 0; for (int i = 0; i < 32; ++i) { // Randomize the length of the blocks. size_t size = rand.nextRangeU(1, sizeof(buffer)); for (size_t j = 0; j < size; ++j) { buffer[j] = valueSource.nextU() & 0xFF; } dynamicMemoryWStream.write(buffer, size); totalWritten += size; REPORTER_ASSERT(rep, totalWritten == dynamicMemoryWStream.bytesWritten()); } std::unique_ptr<SkStreamAsset> asset(dynamicMemoryWStream.detachAsStream()); sk_sp<SkData> expected(SkData::MakeUninitialized(asset->getLength())); uint8_t* expectedPtr = static_cast<uint8_t*>(expected->writable_data()); valueSource.setSeed(kSeed); // reseed. // We want the exact same same "random" string of numbers to put // in expected. i.e.: don't rely on SkDynamicMemoryStream to work // correctly while we are testing SkDynamicMemoryStream. for (size_t i = 0; i < asset->getLength(); ++i) { expectedPtr[i] = valueSource.nextU() & 0xFF; } stream_peek_test(rep, asset.get(), expected.get()); }
static SkString emit_to_string(T& obj) { SkDynamicMemoryWStream buffer; obj.emitObject(&buffer); SkString tmp(buffer.bytesWritten()); buffer.copyTo(tmp.writable_str()); return tmp; }
static void test_empty(skiatest::Reporter* reporter) { SkDynamicMemoryWStream stream; SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream)); doc->close(); REPORTER_ASSERT(reporter, stream.bytesWritten() == 0); }
static void test_empty(skiatest::Reporter* reporter) { SkDynamicMemoryWStream stream; auto doc = SkPDF::MakeDocument(&stream); doc->close(); REPORTER_ASSERT(reporter, stream.bytesWritten() == 0); }
static void apply_paint_typeface(const SkPaint& paint, Json::Value* target, bool sendBinaries) { SkTypeface* typeface = paint.getTypeface(); if (typeface != nullptr) { if (sendBinaries) { Json::Value jsonTypeface; SkDynamicMemoryWStream buffer; typeface->serialize(&buffer); void* data = sk_malloc_throw(buffer.bytesWritten()); buffer.copyTo(data); Json::Value bytes; encode_data(data, buffer.bytesWritten(), &bytes); jsonTypeface[SKJSONCANVAS_ATTRIBUTE_BYTES] = bytes; free(data); (*target)[SKJSONCANVAS_ATTRIBUTE_TYPEFACE] = jsonTypeface; } } }
static SkString emit_to_string(T& obj, Catalog* catPtr = nullptr) { Catalog catalog; SkDynamicMemoryWStream buffer; if (!catPtr) { catPtr = &catalog; } obj.emitObject(&buffer, catPtr->numbers, catPtr->substitutes); SkString tmp(buffer.bytesWritten()); buffer.copyTo(tmp.writable_str()); return tmp; }
// http://crbug.com/473572 DEF_TEST(SkPDF_OpaqueSrcModeToSrcOver, r) { REQUIRE_PDF_DOCUMENT(SkPDF_OpaqueSrcModeToSrcOver, r); SkDynamicMemoryWStream srcMode; SkDynamicMemoryWStream srcOverMode; U8CPU alpha = SK_AlphaOPAQUE; run_test(&srcMode, SkBlendMode::kSrc, alpha); run_test(&srcOverMode, SkBlendMode::kSrcOver, alpha); REPORTER_ASSERT(r, srcMode.bytesWritten() == srcOverMode.bytesWritten()); // The two PDFs should be equal because they have an opaque alpha. srcMode.reset(); srcOverMode.reset(); alpha = 0x80; run_test(&srcMode, SkBlendMode::kSrc, alpha); run_test(&srcOverMode, SkBlendMode::kSrcOver, alpha); REPORTER_ASSERT(r, srcMode.bytesWritten() > srcOverMode.bytesWritten()); // The two PDFs should not be equal because they have a non-opaque alpha. }
static void test_close(skiatest::Reporter* reporter) { SkDynamicMemoryWStream stream; auto doc = SkPDF::MakeDocument(&stream); SkCanvas* canvas = doc->beginPage(100, 100); canvas->drawColor(SK_ColorRED); doc->endPage(); doc->close(); REPORTER_ASSERT(reporter, stream.bytesWritten() != 0); }
static void test_close(skiatest::Reporter* reporter) { SkDynamicMemoryWStream stream; SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(&stream)); SkCanvas* canvas = doc->beginPage(100, 100); canvas->drawColor(SK_ColorRED); doc->endPage(); doc->close(); REPORTER_ASSERT(reporter, stream.bytesWritten() != 0); }
static void test_abort(skiatest::Reporter* reporter) { SkDynamicMemoryWStream stream; auto doc = SkPDF::MakeDocument(&stream); SkCanvas* canvas = doc->beginPage(100, 100); canvas->drawColor(SK_ColorRED); doc->endPage(); doc->abort(); // Test that only the header is written, not the full document. REPORTER_ASSERT(reporter, stream.bytesWritten() < 256); }
size_t count_bytes(const SkBitmap& bm, bool useDCT) { SkDynamicMemoryWStream stream; SkAutoTUnref<SkDocument> doc; if (useDCT) { SkAutoTUnref<SkPixelSerializer> serializer(new JPEGSerializer); doc.reset(SkDocument::CreatePDF( &stream, SK_ScalarDefaultRasterDPI, serializer)); } else { doc.reset(SkDocument::CreatePDF(&stream)); } SkCanvas* canvas = doc->beginPage(64, 64); canvas->drawBitmap(bm, 0, 0); doc->endPage(); doc->close(); return stream.bytesWritten(); }
DEF_TEST(DynamicMemoryWStream_detachAsData, r) { const char az[] = "abcdefghijklmnopqrstuvwxyz"; const unsigned N = 40000; SkDynamicMemoryWStream dmws; for (unsigned i = 0; i < N; ++i) { dmws.writeText(az); } REPORTER_ASSERT(r, dmws.bytesWritten() == N * strlen(az)); auto data = dmws.detachAsData(); REPORTER_ASSERT(r, data->size() == N * strlen(az)); const uint8_t* ptr = data->bytes(); for (unsigned i = 0; i < N; ++i) { if (0 != memcmp(ptr, az, strlen(az))) { ERRORF(r, "detachAsData() memcmp failed"); return; } ptr += strlen(az); } }
void DisplayItemList::checkCachedDisplayItemIsUnchanged(const DisplayItem& displayItem, DisplayItemIndicesByClientMap& displayItemIndicesByClient) { ASSERT(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled()); if (!displayItem.isDrawing() || displayItem.skippedCache() || !clientCacheIsValid(displayItem.client())) return; // If checking under-invalidation, we always generate new display item even if the client is not invalidated. // Checks if the new picture is the same as the cached old picture. If the new picture is different but // the client is not invalidated, issue error about under-invalidation. size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), displayItemIndicesByClient, m_currentDisplayItems); if (index == kNotFound) { showUnderInvalidationError("ERROR: under-invalidation: no cached display item", displayItem); ASSERT_NOT_REACHED(); return; } DisplayItems::iterator foundItem = m_currentDisplayItems.begin() + index; RefPtr<const SkPicture> newPicture = static_cast<const DrawingDisplayItem&>(displayItem).picture(); RefPtr<const SkPicture> oldPicture = static_cast<const DrawingDisplayItem&>(*foundItem).picture(); // Invalidate the display item so that we can check if there are any remaining cached display items after merging. foundItem->clearClientForUnderInvalidationChecking(); if (!newPicture && !oldPicture) return; if (newPicture && oldPicture) { switch (static_cast<const DrawingDisplayItem&>(displayItem).underInvalidationCheckingMode()) { case DrawingDisplayItem::CheckPicture: if (newPicture->approximateOpCount() == oldPicture->approximateOpCount()) { SkDynamicMemoryWStream newPictureSerialized; newPicture->serialize(&newPictureSerialized); SkDynamicMemoryWStream oldPictureSerialized; oldPicture->serialize(&oldPictureSerialized); if (newPictureSerialized.bytesWritten() == oldPictureSerialized.bytesWritten()) { RefPtr<SkData> oldData = adoptRef(oldPictureSerialized.copyToData()); RefPtr<SkData> newData = adoptRef(newPictureSerialized.copyToData()); if (oldData->equals(newData.get())) return; } } break; case DrawingDisplayItem::CheckBitmap: if (newPicture->cullRect() == oldPicture->cullRect()) { SkBitmap bitmap; SkRect rect = newPicture->cullRect(); bitmap.allocPixels(SkImageInfo::MakeN32Premul(rect.width(), rect.height())); SkCanvas canvas(bitmap); canvas.translate(-rect.x(), -rect.y()); canvas.drawPicture(oldPicture.get()); SkPaint diffPaint; diffPaint.setXfermodeMode(SkXfermode::kDifference_Mode); canvas.drawPicture(newPicture.get(), nullptr, &diffPaint); if (bitmapIsAllZero(bitmap)) // Contents are the same. return; } default: ASSERT_NOT_REACHED(); } } showUnderInvalidationError("ERROR: under-invalidation: display item changed", displayItem); #ifndef NDEBUG String oldPictureDebugString = oldPicture ? pictureAsDebugString(oldPicture.get()) : "None"; String newPictureDebugString = newPicture ? pictureAsDebugString(newPicture.get()) : "None"; WTFLogAlways("old picture:\n%s\n", oldPictureDebugString.utf8().data()); WTFLogAlways("new picture:\n%s\n", newPictureDebugString.utf8().data()); #endif // NDEBUG ASSERT_NOT_REACHED(); }
// static void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle, SkWStream* content) { // Filling a path with no area results in a drawing in PDF renderers but // Chrome expects to be able to draw some such entities with no visible // result, so we detect those cases and discard the drawing for them. // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y). enum SkipFillState { kEmpty_SkipFillState = 0, kSingleLine_SkipFillState = 1, kNonSingleLine_SkipFillState = 2, }; SkipFillState fillState = kEmpty_SkipFillState; if (paintStyle != SkPaint::kFill_Style) { fillState = kNonSingleLine_SkipFillState; } SkPoint lastMovePt = SkPoint::Make(0,0); SkDynamicMemoryWStream currentSegment; SkPoint args[4]; SkPath::Iter iter(path, false); for (SkPath::Verb verb = iter.next(args); verb != SkPath::kDone_Verb; verb = iter.next(args)) { // args gets all the points, even the implicit first point. switch (verb) { case SkPath::kMove_Verb: MoveTo(args[0].fX, args[0].fY, ¤tSegment); lastMovePt = args[0]; fillState = kEmpty_SkipFillState; break; case SkPath::kLine_Verb: AppendLine(args[1].fX, args[1].fY, ¤tSegment); if (fillState == kEmpty_SkipFillState) { if (args[0] != lastMovePt) { fillState = kSingleLine_SkipFillState; } } else if (fillState == kSingleLine_SkipFillState) { fillState = kNonSingleLine_SkipFillState; } break; case SkPath::kQuad_Verb: { SkPoint cubic[4]; SkConvertQuadToCubic(args, cubic); AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY, ¤tSegment); fillState = kNonSingleLine_SkipFillState; break; } case SkPath::kCubic_Verb: AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY, args[3].fX, args[3].fY, ¤tSegment); fillState = kNonSingleLine_SkipFillState; break; case SkPath::kClose_Verb: if (fillState != kSingleLine_SkipFillState) { ClosePath(¤tSegment); SkData* data = currentSegment.copyToData(); content->write(data->data(), data->size()); data->unref(); } currentSegment.reset(); break; default: SkASSERT(false); break; } } if (currentSegment.bytesWritten() > 0) { SkData* data = currentSegment.copyToData(); content->write(data->data(), data->size()); data->unref(); } }
static void testPngComments(const SkPixmap& src, SkPngEncoder::Options& options, skiatest::Reporter* r) { std::vector<std::string> commentStrings; pushComment(commentStrings, "key", "text"); pushComment(commentStrings, "test", "something"); pushComment(commentStrings, "have some", "spaces in both"); std::string longKey(PNG_KEYWORD_MAX_LENGTH, 'x'); #ifdef SK_DEBUG commentStrings.push_back(longKey); #else // We call SkDEBUGFAILF it the key is too long so we'll only test this in release mode. commentStrings.push_back(longKey + "x"); #endif commentStrings.push_back(""); std::vector<const char*> commentPointers; std::vector<size_t> commentSizes; for(auto& str : commentStrings) { commentPointers.push_back(str.c_str()); commentSizes.push_back(str.length() + 1); } options.fComments = SkDataTable::MakeCopyArrays((void const *const *)commentPointers.data(), commentSizes.data(), commentStrings.size()); SkDynamicMemoryWStream dst; bool success = SkPngEncoder::Encode(&dst, src, options); REPORTER_ASSERT(r, success); std::vector<char> output(dst.bytesWritten()); dst.copyTo(output.data()); // Each chunk is of the form length (4 bytes), chunk type (tEXt), data, // checksum (4 bytes). Make sure we find all of them in the encoded // results. const char kExpected1[] = "\x00\x00\x00\x08tEXtkey\x00text\x9e\xe7\x66\x51"; const char kExpected2[] = "\x00\x00\x00\x0etEXttest\x00something\x29\xba\xef\xac"; const char kExpected3[] = "\x00\x00\x00\x18tEXthave some\x00spaces in both\x8d\x69\x34\x2d"; std::string longKeyRecord = "tEXt" + longKey; // A snippet of our long key comment std::string tooLongRecord = "tExt" + longKey + "x"; // A snippet whose key is too long auto search1 = std::search(output.begin(), output.end(), kExpected1, kExpected1 + sizeof(kExpected1)); auto search2 = std::search(output.begin(), output.end(), kExpected2, kExpected2 + sizeof(kExpected2)); auto search3 = std::search(output.begin(), output.end(), kExpected3, kExpected3 + sizeof(kExpected3)); auto search4 = std::search(output.begin(), output.end(), longKeyRecord.begin(), longKeyRecord.end()); auto search5 = std::search(output.begin(), output.end(), tooLongRecord.begin(), tooLongRecord.end()); REPORTER_ASSERT(r, search1 != output.end()); REPORTER_ASSERT(r, search2 != output.end()); REPORTER_ASSERT(r, search3 != output.end()); REPORTER_ASSERT(r, search4 != output.end()); REPORTER_ASSERT(r, search5 == output.end()); // Comments test ends }