Switch SkPDFStream's internal storage from SkStream to SkData

Motivation: This makes SkPDFStream thread-safe for two threads
serializing it at once, since a SkStream has an internal position.

Updated SkPDFFont, SkPDFGraphicState, and SkPDFPage's use of
SkPDFStream to use the SkData constructor rather than the SkStream
constructor (saving a memcpy).

BUG=skia:2683
R=mtklein@google.com, djsollen@google.com

Author: halcanary@google.com

Review URL: https://codereview.chromium.org/340783013
This commit is contained in:
halcanary 2014-06-26 14:00:31 -07:00 committed by Commit bot
parent 11a005ee01
commit c1dfa14b64
7 changed files with 86 additions and 63 deletions

View File

@ -18,6 +18,7 @@
'../src/core', # needed to get SkGlyphCache.h and SkTextFormatParams.h '../src/core', # needed to get SkGlyphCache.h and SkTextFormatParams.h
'../src/pdf', '../src/pdf',
'../src/utils', # needed to get SkBitSet.h '../src/utils', # needed to get SkBitSet.h
'../src/images', # needed to get SkStreamHelpers.h
], ],
'sources': [ 'sources': [
'pdf.gypi', # Makes the gypi appear in IDEs (but does not modify the build). 'pdf.gypi', # Makes the gypi appear in IDEs (but does not modify the build).

View File

@ -150,8 +150,8 @@ int8_t hexToBin(uint8_t c) {
return -1; return -1;
} }
SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen, static SkData* handle_type1_stream(SkStream* srcStream, size_t* headerLen,
size_t* dataLen, size_t* trailerLen) { size_t* dataLen, size_t* trailerLen) {
// srcStream may be backed by a file or a unseekable fd, so we may not be // srcStream may be backed by a file or a unseekable fd, so we may not be
// able to use skip(), rewind(), or getMemoryBase(). read()ing through // able to use skip(), rewind(), or getMemoryBase(). read()ing through
// the input only once is doable, but very ugly. Furthermore, it'd be nice // the input only once is doable, but very ugly. Furthermore, it'd be nice
@ -199,26 +199,43 @@ SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen,
SkAutoDataUnref aud(data); SkAutoDataUnref aud(data);
if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) { if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) {
SkMemoryStream* result = static const int kPFBSectionHeaderLength = 6;
new SkMemoryStream(*headerLen + *dataLen + *trailerLen); const size_t length = *headerLen + *dataLen + *trailerLen;
memcpy((char*)result->getAtPos(), src + 6, *headerLen); SkASSERT(length > 0);
result->seek(*headerLen); SkASSERT(length + (2 * kPFBSectionHeaderLength) <= srcLen);
memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6, *dataLen);
result->seek(*headerLen + *dataLen); SkAutoTMalloc<uint8_t> buffer(length);
memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6 + *dataLen,
*trailerLen); const uint8_t* const srcHeader = src + kPFBSectionHeaderLength;
result->rewind(); // There is a six-byte section header before header and data
return result; // (but not trailer) that we're not going to copy.
const uint8_t* const srcData
= srcHeader + *headerLen + kPFBSectionHeaderLength;
const uint8_t* const srcTrailer = srcData + *headerLen;
uint8_t* const resultHeader = buffer.get();
uint8_t* const resultData = resultHeader + *headerLen;
uint8_t* const resultTrailer = resultData + *dataLen;
SkASSERT(resultTrailer + *trailerLen == resultHeader + length);
memcpy(resultHeader, srcHeader, *headerLen);
memcpy(resultData, srcData, *dataLen);
memcpy(resultTrailer, srcTrailer, *trailerLen);
return SkData::NewFromMalloc(buffer.detach(), length);
} }
// A PFA has to be converted for PDF. // A PFA has to be converted for PDF.
size_t hexDataLen; size_t hexDataLen;
if (parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen, if (parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen,
trailerLen)) { trailerLen)) {
SkMemoryStream* result = const size_t length = *headerLen + *dataLen + *trailerLen;
new SkMemoryStream(*headerLen + *dataLen + *trailerLen); SkASSERT(length > 0);
memcpy((char*)result->getAtPos(), src, *headerLen); SkAutoTMalloc<uint8_t> buffer(length);
result->seek(*headerLen);
memcpy(buffer.get(), src, *headerLen);
uint8_t* const resultData = &(buffer[*headerLen]);
const uint8_t* hexData = src + *headerLen; const uint8_t* hexData = src + *headerLen;
const uint8_t* trailer = hexData + hexDataLen; const uint8_t* trailer = hexData + hexDataLen;
@ -236,21 +253,19 @@ SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen,
} else { } else {
dataByte |= curNibble; dataByte |= curNibble;
highNibble = true; highNibble = true;
((char *)result->getAtPos())[outputOffset++] = dataByte; resultData[outputOffset++] = dataByte;
} }
} }
if (!highNibble) { if (!highNibble) {
((char *)result->getAtPos())[outputOffset++] = dataByte; resultData[outputOffset++] = dataByte;
} }
SkASSERT(outputOffset == *dataLen); SkASSERT(outputOffset == *dataLen);
result->seek(*headerLen + outputOffset);
memcpy((char *)result->getAtPos(), src + *headerLen + hexDataLen, uint8_t* const resultTrailer = &(buffer[*headerLen + outputOffset]);
*trailerLen); memcpy(resultTrailer, src + *headerLen + hexDataLen, *trailerLen);
result->rewind();
return result; return SkData::NewFromMalloc(buffer.detach(), length);
} }
return NULL; return NULL;
} }
@ -556,9 +571,8 @@ static SkPDFStream* generate_tounicode_cmap(
append_cmap_sections(glyphToUnicode, subset, &cmap, multiByteGlyphs, append_cmap_sections(glyphToUnicode, subset, &cmap, multiByteGlyphs,
firstGlyphID, lastGlyphID); firstGlyphID, lastGlyphID);
append_cmap_footer(&cmap); append_cmap_footer(&cmap);
SkAutoTUnref<SkMemoryStream> cmapStream(new SkMemoryStream()); SkAutoTUnref<SkData> cmapData(cmap.copyToData());
cmapStream->setData(cmap.copyToData())->unref(); return new SkPDFStream(cmapData.get());
return new SkPDFStream(cmapStream.get());
} }
#if defined (SK_SFNTLY_SUBSETTER) #if defined (SK_SFNTLY_SUBSETTER)
@ -574,6 +588,7 @@ static size_t get_subset_font_stream(const char* fontName,
SkPDFStream** fontStream) { SkPDFStream** fontStream) {
int ttcIndex; int ttcIndex;
SkAutoTUnref<SkStream> fontData(typeface->openStream(&ttcIndex)); SkAutoTUnref<SkStream> fontData(typeface->openStream(&ttcIndex));
SkASSERT(fontData.get());
size_t fontSize = fontData->getLength(); size_t fontSize = fontData->getLength();
@ -1295,7 +1310,7 @@ bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
size_t data SK_INIT_TO_AVOID_WARNING; size_t data SK_INIT_TO_AVOID_WARNING;
size_t trailer SK_INIT_TO_AVOID_WARNING; size_t trailer SK_INIT_TO_AVOID_WARNING;
SkAutoTUnref<SkStream> rawFontData(typeface()->openStream(&ttcIndex)); SkAutoTUnref<SkStream> rawFontData(typeface()->openStream(&ttcIndex));
SkStream* fontData = handleType1Stream(rawFontData.get(), &header, &data, SkData* fontData = handle_type1_stream(rawFontData.get(), &header, &data,
&trailer); &trailer);
if (fontData == NULL) { if (fontData == NULL) {
return false; return false;

View File

@ -5,10 +5,10 @@
* found in the LICENSE file. * found in the LICENSE file.
*/ */
#include "SkData.h"
#include "SkPDFFormXObject.h" #include "SkPDFFormXObject.h"
#include "SkPDFGraphicState.h" #include "SkPDFGraphicState.h"
#include "SkPDFUtils.h" #include "SkPDFUtils.h"
#include "SkStream.h"
#include "SkTypes.h" #include "SkTypes.h"
static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) { static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
@ -121,8 +121,9 @@ SkPDFObject* SkPDFGraphicState::GetInvertFunction() {
domainAndRange->appendInt(1); domainAndRange->appendInt(1);
static const char psInvert[] = "{1 exch sub}"; static const char psInvert[] = "{1 exch sub}";
SkAutoTUnref<SkMemoryStream> psInvertStream( // Do not copy the trailing '\0' into the SkData.
new SkMemoryStream(&psInvert, strlen(psInvert), true)); SkAutoTUnref<SkData> psInvertStream(
SkData::NewWithCopy(psInvert, strlen(psInvert)));
invertFunction = new SkPDFStream(psInvertStream.get()); invertFunction = new SkPDFStream(psInvertStream.get());
invertFunction->insertInt("FunctionType", 4); invertFunction->insertInt("FunctionType", 4);

View File

@ -512,7 +512,7 @@ SkPDFImage::SkPDFImage(SkStream* stream,
} }
if (stream != NULL) { if (stream != NULL) {
setData(stream); this->setData(stream);
fStreamValid = true; fStreamValid = true;
} else { } else {
fStreamValid = false; fStreamValid = false;
@ -598,13 +598,11 @@ bool SkPDFImage::populate(SkPDFCatalog* catalog) {
SkAutoTUnref<SkData> data(fEncoder(&pixelRefOffset, subset)); SkAutoTUnref<SkData> data(fEncoder(&pixelRefOffset, subset));
if (data.get() && data->size() < get_uncompressed_size(fBitmap, if (data.get() && data->size() < get_uncompressed_size(fBitmap,
fSrcRect)) { fSrcRect)) {
SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, this->setData(data.get());
(data)));
setData(stream.get());
insertName("Filter", "DCTDecode"); insertName("Filter", "DCTDecode");
insertInt("ColorTransform", kNoColorTransform); insertInt("ColorTransform", kNoColorTransform);
insertInt("Length", getData()->getLength()); insertInt("Length", this->dataSize());
setState(kCompressed_State); setState(kCompressed_State);
return true; return true;
} }
@ -613,7 +611,7 @@ bool SkPDFImage::populate(SkPDFCatalog* catalog) {
if (!fStreamValid) { if (!fStreamValid) {
SkAutoTUnref<SkStream> stream( SkAutoTUnref<SkStream> stream(
extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL)); extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL));
setData(stream); this->setData(stream);
fStreamValid = true; fStreamValid = true;
} }
return INHERITED::populate(catalog); return INHERITED::populate(catalog);

View File

@ -7,11 +7,11 @@
*/ */
#include "SkData.h"
#include "SkPDFCatalog.h" #include "SkPDFCatalog.h"
#include "SkPDFDevice.h" #include "SkPDFDevice.h"
#include "SkPDFPage.h" #include "SkPDFPage.h"
#include "SkPDFResourceDict.h" #include "SkPDFResourceDict.h"
#include "SkStream.h"
SkPDFPage::SkPDFPage(SkPDFDevice* content) SkPDFPage::SkPDFPage(SkPDFDevice* content)
: SkPDFDict("Page"), : SkPDFDict("Page"),
@ -36,7 +36,7 @@ void SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage,
} }
} }
SkAutoTUnref<SkStream> content(fDevice->content()); SkAutoTUnref<SkData> content(fDevice->copyContentToData());
fContentStream.reset(new SkPDFStream(content.get())); fContentStream.reset(new SkPDFStream(content.get()));
insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref(); insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
} }

View File

@ -12,6 +12,7 @@
#include "SkPDFCatalog.h" #include "SkPDFCatalog.h"
#include "SkPDFStream.h" #include "SkPDFStream.h"
#include "SkStream.h" #include "SkStream.h"
#include "SkStreamHelpers.h" // CopyStreamToData
static bool skip_compression(SkPDFCatalog* catalog) { static bool skip_compression(SkPDFCatalog* catalog) {
return SkToBool(catalog->getDocumentFlags() & return SkToBool(catalog->getDocumentFlags() &
@ -19,17 +20,17 @@ static bool skip_compression(SkPDFCatalog* catalog) {
} }
SkPDFStream::SkPDFStream(SkStream* stream) : fState(kUnused_State) { SkPDFStream::SkPDFStream(SkStream* stream) : fState(kUnused_State) {
setData(stream); this->setData(stream);
} }
SkPDFStream::SkPDFStream(SkData* data) : fState(kUnused_State) { SkPDFStream::SkPDFStream(SkData* data) : fState(kUnused_State) {
setData(data); this->setData(data);
} }
SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream) SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream)
: SkPDFDict(), : SkPDFDict(),
fState(kUnused_State) { fState(kUnused_State) {
setData(pdfStream.fData.get()); this->setData(pdfStream.fData.get());
bool removeLength = true; bool removeLength = true;
// Don't uncompress an already compressed stream, but we could. // Don't uncompress an already compressed stream, but we could.
if (pdfStream.fState == kCompressed_State) { if (pdfStream.fState == kCompressed_State) {
@ -55,14 +56,16 @@ void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
if (indirect) { if (indirect) {
return emitIndirectObject(stream, catalog); return emitIndirectObject(stream, catalog);
} }
SkAutoMutexAcquire lock(fMutex); // multiple threads could be calling emit
if (!this->populate(catalog)) { if (!this->populate(catalog)) {
return fSubstitute->emitObject(stream, catalog, indirect); return fSubstitute->emitObject(stream, catalog, indirect);
} }
this->INHERITED::emitObject(stream, catalog, false); this->INHERITED::emitObject(stream, catalog, false);
stream->writeText(" stream\n"); stream->writeText(" stream\n");
stream->writeStream(fData.get(), fData->getLength()); if (fData.get()) {
fData->rewind(); stream->write(fData->data(), fData->size());
}
stream->writeText("\nendstream"); stream->writeText("\nendstream");
} }
@ -70,30 +73,34 @@ size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
if (indirect) { if (indirect) {
return getIndirectOutputSize(catalog); return getIndirectOutputSize(catalog);
} }
SkAutoMutexAcquire lock(fMutex); // multiple threads could be calling emit
if (!this->populate(catalog)) { if (!this->populate(catalog)) {
return fSubstitute->getOutputSize(catalog, indirect); return fSubstitute->getOutputSize(catalog, indirect);
} }
return this->INHERITED::getOutputSize(catalog, false) + return this->INHERITED::getOutputSize(catalog, false) +
strlen(" stream\n\nendstream") + fData->getLength(); strlen(" stream\n\nendstream") + this->dataSize();
} }
SkPDFStream::SkPDFStream() : fState(kUnused_State) {} SkPDFStream::SkPDFStream() : fState(kUnused_State) {}
void SkPDFStream::setData(SkData* data) { void SkPDFStream::setData(SkData* data) {
SkMemoryStream* stream = new SkMemoryStream; fData.reset(SkSafeRef(data));
stream->setData(data);
fData.reset(stream); // Transfer ownership.
} }
void SkPDFStream::setData(SkStream* stream) { void SkPDFStream::setData(SkStream* stream) {
// Code assumes that the stream starts at the beginning and is rewindable. // Code assumes that the stream starts at the beginning and is rewindable.
if (stream) { if (stream) {
SkASSERT(stream->getPosition() == 0); SkASSERT(stream->getPosition() == 0);
fData.reset(CopyStreamToData(stream));
SkASSERT(stream->rewind()); SkASSERT(stream->rewind());
} else {
fData.reset(NULL);
} }
fData.reset(stream); }
SkSafeRef(stream);
size_t SkPDFStream::dataSize() const {
return fData.get() ? fData->size() : 0;
} }
bool SkPDFStream::populate(SkPDFCatalog* catalog) { bool SkPDFStream::populate(SkPDFCatalog* catalog) {
@ -102,17 +109,15 @@ bool SkPDFStream::populate(SkPDFCatalog* catalog) {
SkDynamicMemoryWStream compressedData; SkDynamicMemoryWStream compressedData;
SkAssertResult(SkFlate::Deflate(fData.get(), &compressedData)); SkAssertResult(SkFlate::Deflate(fData.get(), &compressedData));
if (compressedData.getOffset() < fData->getLength()) { if (compressedData.getOffset() < this->dataSize()) {
SkMemoryStream* stream = new SkMemoryStream; fData.reset(compressedData.copyToData());
stream->setData(compressedData.copyToData())->unref();
fData.reset(stream); // Transfer ownership.
insertName("Filter", "FlateDecode"); insertName("Filter", "FlateDecode");
} }
fState = kCompressed_State; fState = kCompressed_State;
} else { } else {
fState = kNoCompression_State; fState = kNoCompression_State;
} }
insertInt("Length", fData->getLength()); insertInt("Length", this->dataSize());
} else if (fState == kNoCompression_State && !skip_compression(catalog) && } else if (fState == kNoCompression_State && !skip_compression(catalog) &&
SkFlate::HaveFlate()) { SkFlate::HaveFlate()) {
if (!fSubstitute.get()) { if (!fSubstitute.get()) {

View File

@ -41,7 +41,8 @@ public:
explicit SkPDFStream(const SkPDFStream& pdfStream); explicit SkPDFStream(const SkPDFStream& pdfStream);
virtual ~SkPDFStream(); virtual ~SkPDFStream();
// The SkPDFObject interface. // The SkPDFObject interface. These two methods use a mutex to
// allow multiple threads to call at the same time.
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog, virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
bool indirect); bool indirect);
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect); virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
@ -67,22 +68,22 @@ protected:
fSubstitute.reset(stream); fSubstitute.reset(stream);
} }
SkPDFStream* getSubstitute() { SkPDFStream* getSubstitute() const {
return fSubstitute.get(); return fSubstitute.get();
} }
void setData(SkData* data); void setData(SkData* data);
void setData(SkStream* stream); void setData(SkStream* stream);
SkStream* getData() { size_t dataSize() const;
return fData.get();
} SkData* getData() const { return fData.get(); }
void setState(State state) { void setState(State state) {
fState = state; fState = state;
} }
State getState() { State getState() const {
return fState; return fState;
} }
@ -90,8 +91,10 @@ private:
// Indicates what form (or if) the stream has been requested. // Indicates what form (or if) the stream has been requested.
State fState; State fState;
// TODO(vandebo): Use SkData (after removing deprecated constructor). // Mutex guards fState, fData, and fSubstitute in public interface.
SkAutoTUnref<SkStream> fData; SkMutex fMutex;
SkAutoTUnref<SkData> fData;
SkAutoTUnref<SkPDFStream> fSubstitute; SkAutoTUnref<SkPDFStream> fSubstitute;
typedef SkPDFDict INHERITED; typedef SkPDFDict INHERITED;