SkPDF: refactor generate_page_tree

function now takes a constant ref, as needed.

re-written to make it easier to think about state.

Change-Id: I5b9935cd88b4f8b1f35d4df622adcfb61655d42b
Reviewed-on: https://skia-review.googlesource.com/c/160383
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Hal Canary <halcanary@google.com>
This commit is contained in:
Hal Canary 2018-10-08 10:17:43 -04:00 committed by Skia Commit-Bot
parent bbf5fb5be4
commit 36fe1d6e65

View File

@ -119,9 +119,7 @@ int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) {
return SkToS32(offset - fBaseOffset); return SkToS32(offset - fBaseOffset);
} }
static sk_sp<SkPDFDict> generate_page_tree(const std::vector<sk_sp<SkPDFDict>>& pages) {
// return root node.
static sk_sp<SkPDFDict> generate_page_tree(std::vector<sk_sp<SkPDFDict>>* pages) {
// PDF wants a tree describing all the pages in the document. We arbitrary // PDF wants a tree describing all the pages in the document. We arbitrary
// choose 8 (kNodeSize) as the number of allowed children. The internal // choose 8 (kNodeSize) as the number of allowed children. The internal
// nodes have type "Pages" with an array of children, a parent pointer, and // nodes have type "Pages" with an array of children, a parent pointer, and
@ -129,57 +127,53 @@ static sk_sp<SkPDFDict> generate_page_tree(std::vector<sk_sp<SkPDFDict>>* pages)
// into the method, have type "Page" and need a parent pointer. This method // into the method, have type "Page" and need a parent pointer. This method
// builds the tree bottom up, skipping internal nodes that would have only // builds the tree bottom up, skipping internal nodes that would have only
// one child. // one child.
static const int kNodeSize = 8; SkASSERT(pages.size() > 0);
struct PageTreeNode {
sk_sp<SkPDFDict> fNode;
int fPageObjectDescendantCount;
// curNodes takes a reference to its items, which it passes to pageTree. static void Layer(std::vector<PageTreeNode>* vec) {
size_t totalPageCount = pages->size(); std::vector<PageTreeNode> result;
std::vector<sk_sp<SkPDFDict>> curNodes; static constexpr size_t kMaxNodeSize = 8;
curNodes.swap(*pages); const size_t n = vec->size();
SkASSERT(n >= 1);
// nextRoundNodes passes its references to nodes on to curNodes. const size_t result_len = (n - 1) / kMaxNodeSize + 1;
int treeCapacity = kNodeSize; SkASSERT(result_len >= 1);
do { SkASSERT(n == 1 || result_len < n);
std::vector<sk_sp<SkPDFDict>> nextRoundNodes; result.reserve(result_len);
for (size_t i = 0; i < curNodes.size(); ) { size_t index = 0;
if (i > 0 && i + 1 == curNodes.size()) { for (size_t i = 0; i < result_len; ++i) {
SkASSERT(curNodes[i]); if (n != 1 && index + 1 == n) { // No need to create a new node.
nextRoundNodes.emplace_back(std::move(curNodes[i])); result.push_back(std::move((*vec)[index++]));
break; continue;
} }
auto next = sk_make_sp<SkPDFDict>("Pages");
auto newNode = sk_make_sp<SkPDFDict>("Pages"); auto kids_list = sk_make_sp<SkPDFArray>();
auto kids = sk_make_sp<SkPDFArray>(); int descendantCount = 0;
kids->reserve(kNodeSize); for (size_t j = 0; j < kMaxNodeSize && index < n; ++j) {
PageTreeNode& node = (*vec)[index++];
int count = 0; node.fNode->insertObjRef("Parent", next);
for (; i < curNodes.size() && count < kNodeSize; i++, count++) { kids_list->appendObjRef(std::move(node.fNode));
SkASSERT(curNodes[i]); descendantCount += node.fPageObjectDescendantCount;
curNodes[i]->insertObjRef("Parent", newNode);
kids->appendObjRef(std::move(curNodes[i]));
} }
next->insertInt("Count", descendantCount);
// treeCapacity is the number of leaf nodes possible for the next->insertObject("Kids", std::move(kids_list));
// current set of subtrees being generated. (i.e. 8, 64, 512, ...). result.push_back(PageTreeNode{std::move(next), descendantCount});
// It is hard to count the number of leaf nodes in the current
// subtree. However, by construction, we know that unless it's the
// last subtree for the current depth, the leaf count will be
// treeCapacity, otherwise it's what ever is left over after
// consuming treeCapacity chunks.
size_t pageCount = treeCapacity;
if (i == curNodes.size()) {
pageCount = ((totalPageCount - 1) % treeCapacity) + 1;
} }
newNode->insertInt("Count", SkToInt(pageCount)); *vec = result;
newNode->insertObject("Kids", std::move(kids));
nextRoundNodes.emplace_back(std::move(newNode));
} }
SkDEBUGCODE( for (const auto& n : curNodes) { SkASSERT(!n); } ); };
std::vector<PageTreeNode> currentLayer;
curNodes.swap(nextRoundNodes); currentLayer.reserve(pages.size());
nextRoundNodes = std::vector<sk_sp<SkPDFDict>>(); for (const sk_sp<SkPDFDict>& page : pages) {
treeCapacity *= kNodeSize; currentLayer.push_back(PageTreeNode{page, 1});
} while (curNodes.size() > 1); }
return std::move(curNodes[0]); PageTreeNode::Layer(&currentLayer);
while (currentLayer.size() > 1) {
PageTreeNode::Layer(&currentLayer);
}
SkASSERT(currentLayer.size() == 1);
return std::move(currentLayer[0].fNode);
} }
template<typename T, typename... Args> template<typename T, typename... Args>
@ -496,12 +490,9 @@ void SkPDFDocument::onClose(SkWStream* stream) {
docCatalog->insertObject("OutputIntents", make_srgb_output_intents()); docCatalog->insertObject("OutputIntents", make_srgb_output_intents());
} }
std::vector<sk_sp<SkPDFDict>> pagesCopy(fPages); sk_sp<SkPDFDict> pageTree = generate_page_tree(fPages);
SkASSERT(!pagesCopy.empty());
sk_sp<SkPDFDict> pageTree = generate_page_tree(&pagesCopy);
pageTree->insertObject("Resources", make_top_resource_dict()); pageTree->insertObject("Resources", make_top_resource_dict());
docCatalog->insertObjRef("Pages", std::move(pageTree)); docCatalog->insertObjRef("Pages", std::move(pageTree));
SkASSERT(pagesCopy.empty());
if (fDests->size() > 0) { if (fDests->size() > 0) {
docCatalog->insertObjRef("Dests", std::move(fDests)); docCatalog->insertObjRef("Dests", std::move(fDests));
} }