SkPDF/Deflate: clean up old SkFlate code
Factor out some of https://crrev.com/1227913008 BUG=skia:3030 Review URL: https://codereview.chromium.org/1298243002
This commit is contained in:
parent
a44919ea27
commit
48305e8353
@ -27,6 +27,7 @@
|
||||
'tools.gyp:picture_utils',
|
||||
'tools.gyp:resources',
|
||||
'tools.gyp:sk_tool_utils',
|
||||
'zlib.gyp:zlib',
|
||||
],
|
||||
'conditions': [
|
||||
[ 'skia_os not in ["linux", "freebsd", "openbsd", "solaris", "chromeos", "android"]', {
|
||||
@ -38,7 +39,7 @@
|
||||
],
|
||||
}],
|
||||
[ 'not skia_pdf', {
|
||||
'dependencies!': [ 'pdf.gyp:pdf' ],
|
||||
'dependencies!': [ 'pdf.gyp:pdf', 'zlib.gyp:zlib' ],
|
||||
'dependencies': [ 'pdf.gyp:nopdf' ],
|
||||
'sources!': [ '<!@(python find.py ../tests "PDF*.c*")', ],
|
||||
}],
|
||||
|
@ -950,3 +950,25 @@ SkStreamRewindable* SkStreamRewindableFromSkStream(SkStream* stream) {
|
||||
return tempStream.detachAsStream(); // returns a SkBlockMemoryStream,
|
||||
// cheaper than copying to SkData
|
||||
}
|
||||
|
||||
bool SkStreamCopy(SkWStream* out, SkStream* input) {
|
||||
const char* base = static_cast<const char*>(input->getMemoryBase());
|
||||
if (base && input->hasPosition() && input->hasLength()) {
|
||||
// Shortcut that avoids the while loop.
|
||||
size_t position = input->getPosition();
|
||||
size_t length = input->getLength();
|
||||
SkASSERT(length >= position);
|
||||
return out->write(&base[position], length - position);
|
||||
}
|
||||
char scratch[4096];
|
||||
size_t count;
|
||||
while (true) {
|
||||
count = input->read(scratch, sizeof(scratch));
|
||||
if (0 == count) {
|
||||
return true;
|
||||
}
|
||||
if (!out->write(scratch, count)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,4 +43,10 @@ SkData *SkCopyStreamToData(SkStream* stream);
|
||||
*/
|
||||
SkStreamRewindable* SkStreamRewindableFromSkStream(SkStream* stream);
|
||||
|
||||
/**
|
||||
* Copies the input stream from the current position to the end.
|
||||
* Does not rewind the input stream.
|
||||
*/
|
||||
bool SkStreamCopy(SkWStream* out, SkStream* input);
|
||||
|
||||
#endif // SkStreamPriv_DEFINED
|
||||
|
@ -19,120 +19,15 @@ namespace {
|
||||
#include "zlib.h"
|
||||
#endif
|
||||
|
||||
// static
|
||||
const size_t kBufferSize = 1024;
|
||||
|
||||
// Different zlib implementations use different T.
|
||||
// We've seen size_t and unsigned.
|
||||
template <typename T> void* skia_alloc_func(void*, T items, T size) {
|
||||
return sk_calloc_throw(SkToSizeT(items) * SkToSizeT(size));
|
||||
}
|
||||
|
||||
static void skia_free_func(void*, void* address) { sk_free(address); }
|
||||
|
||||
bool doFlate(bool compress, SkStream* src, SkWStream* dst) {
|
||||
uint8_t inputBuffer[kBufferSize];
|
||||
uint8_t outputBuffer[kBufferSize];
|
||||
z_stream flateData;
|
||||
flateData.zalloc = &skia_alloc_func;
|
||||
flateData.zfree = &skia_free_func;
|
||||
flateData.opaque = NULL;
|
||||
flateData.next_in = NULL;
|
||||
flateData.avail_in = 0;
|
||||
flateData.next_out = outputBuffer;
|
||||
flateData.avail_out = kBufferSize;
|
||||
int rc;
|
||||
if (compress)
|
||||
rc = deflateInit(&flateData, Z_DEFAULT_COMPRESSION);
|
||||
else
|
||||
rc = inflateInit(&flateData);
|
||||
if (rc != Z_OK)
|
||||
return false;
|
||||
|
||||
uint8_t* input = (uint8_t*)src->getMemoryBase();
|
||||
size_t inputLength = src->getLength();
|
||||
if (input == NULL || inputLength == 0) {
|
||||
input = NULL;
|
||||
flateData.next_in = inputBuffer;
|
||||
flateData.avail_in = 0;
|
||||
} else {
|
||||
flateData.next_in = input;
|
||||
flateData.avail_in = SkToUInt(inputLength);
|
||||
}
|
||||
|
||||
rc = Z_OK;
|
||||
while (true) {
|
||||
if (flateData.avail_out < kBufferSize) {
|
||||
if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) {
|
||||
rc = Z_BUF_ERROR;
|
||||
break;
|
||||
}
|
||||
flateData.next_out = outputBuffer;
|
||||
flateData.avail_out = kBufferSize;
|
||||
}
|
||||
if (rc != Z_OK)
|
||||
break;
|
||||
if (flateData.avail_in == 0) {
|
||||
if (input != NULL)
|
||||
break;
|
||||
size_t read = src->read(&inputBuffer, kBufferSize);
|
||||
if (read == 0)
|
||||
break;
|
||||
flateData.next_in = inputBuffer;
|
||||
flateData.avail_in = SkToUInt(read);
|
||||
}
|
||||
if (compress)
|
||||
rc = deflate(&flateData, Z_NO_FLUSH);
|
||||
else
|
||||
rc = inflate(&flateData, Z_NO_FLUSH);
|
||||
}
|
||||
while (rc == Z_OK) {
|
||||
if (compress)
|
||||
rc = deflate(&flateData, Z_FINISH);
|
||||
else
|
||||
rc = inflate(&flateData, Z_FINISH);
|
||||
if (flateData.avail_out < kBufferSize) {
|
||||
if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out))
|
||||
return false;
|
||||
flateData.next_out = outputBuffer;
|
||||
flateData.avail_out = kBufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (compress)
|
||||
deflateEnd(&flateData);
|
||||
else
|
||||
inflateEnd(&flateData);
|
||||
if (rc == Z_STREAM_END)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// static
|
||||
bool SkFlate::Deflate(SkStream* src, SkWStream* dst) {
|
||||
return doFlate(true, src, dst);
|
||||
}
|
||||
|
||||
bool SkFlate::Deflate(const void* ptr, size_t len, SkWStream* dst) {
|
||||
SkMemoryStream stream(ptr, len);
|
||||
return doFlate(true, &stream, dst);
|
||||
}
|
||||
|
||||
bool SkFlate::Deflate(const SkData* data, SkWStream* dst) {
|
||||
if (data) {
|
||||
SkMemoryStream stream(data->data(), data->size());
|
||||
return doFlate(true, &stream, dst);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SkFlate::Inflate(SkStream* src, SkWStream* dst) {
|
||||
return doFlate(false, src, dst);
|
||||
}
|
||||
void skia_free_func(void*, void* address) { sk_free(address); }
|
||||
|
||||
} // namespace
|
||||
|
||||
#define SKDEFLATEWSTREAM_INPUT_BUFFER_SIZE 4096
|
||||
#define SKDEFLATEWSTREAM_OUTPUT_BUFFER_SIZE 4224 // 4096 + 128, usually big
|
||||
|
@ -13,36 +13,6 @@
|
||||
#include "SkTypes.h"
|
||||
|
||||
#include "SkStream.h"
|
||||
class SkData;
|
||||
|
||||
/** \class SkFlate
|
||||
A class to provide access to the flate compression algorithm.
|
||||
*/
|
||||
class SkFlate {
|
||||
public:
|
||||
/**
|
||||
* Use the flate compression algorithm to compress the data in src,
|
||||
* putting the result into dst. Returns false if an error occurs.
|
||||
*/
|
||||
static bool Deflate(SkStream* src, SkWStream* dst);
|
||||
|
||||
/**
|
||||
* Use the flate compression algorithm to compress the data in src,
|
||||
* putting the result into dst. Returns false if an error occurs.
|
||||
*/
|
||||
static bool Deflate(const void* src, size_t len, SkWStream* dst);
|
||||
|
||||
/**
|
||||
* Use the flate compression algorithm to compress the data,
|
||||
* putting the result into dst. Returns false if an error occurs.
|
||||
*/
|
||||
static bool Deflate(const SkData*, SkWStream* dst);
|
||||
|
||||
/** Use the flate compression algorithm to decompress the data in src,
|
||||
putting the result into dst. Returns false if an error occurs.
|
||||
*/
|
||||
static bool Inflate(SkStream* src, SkWStream* dst);
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrap a stream in this class to compress the information written to
|
||||
|
@ -29,9 +29,9 @@ void SkPDFStream::emitObject(SkWStream* stream,
|
||||
if (fState == kUnused_State) {
|
||||
fState = kNoCompression_State;
|
||||
SkDynamicMemoryWStream compressedData;
|
||||
|
||||
SkAssertResult(
|
||||
SkFlate::Deflate(fDataStream.get(), &compressedData));
|
||||
SkDeflateWStream deflateWStream(&compressedData);
|
||||
SkAssertResult(SkStreamCopy(&deflateWStream, fDataStream.get()));
|
||||
deflateWStream.finalize();
|
||||
SkAssertResult(fDataStream->rewind());
|
||||
if (compressedData.getOffset() < this->dataSize()) {
|
||||
SkAutoTDelete<SkStream> compressed(
|
||||
|
@ -9,6 +9,98 @@
|
||||
#include "SkRandom.h"
|
||||
#include "Test.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef ZLIB_INCLUDE
|
||||
#include ZLIB_INCLUDE
|
||||
#else
|
||||
#include "zlib.h"
|
||||
#endif
|
||||
|
||||
// Different zlib implementations use different T.
|
||||
// We've seen size_t and unsigned.
|
||||
template <typename T> void* skia_alloc_func(void*, T items, T size) {
|
||||
return sk_calloc_throw(SkToSizeT(items) * SkToSizeT(size));
|
||||
}
|
||||
|
||||
void skia_free_func(void*, void* address) { sk_free(address); }
|
||||
|
||||
/**
|
||||
* Use the un-deflate compression algorithm to decompress the data in src,
|
||||
* returning the result. Returns NULL if an error occurs.
|
||||
*/
|
||||
SkStreamAsset* stream_inflate(SkStream* src) {
|
||||
SkDynamicMemoryWStream decompressedDynamicMemoryWStream;
|
||||
SkWStream* dst = &decompressedDynamicMemoryWStream;
|
||||
|
||||
static const size_t kBufferSize = 1024;
|
||||
uint8_t inputBuffer[kBufferSize];
|
||||
uint8_t outputBuffer[kBufferSize];
|
||||
z_stream flateData;
|
||||
flateData.zalloc = &skia_alloc_func;
|
||||
flateData.zfree = &skia_free_func;
|
||||
flateData.opaque = NULL;
|
||||
flateData.next_in = NULL;
|
||||
flateData.avail_in = 0;
|
||||
flateData.next_out = outputBuffer;
|
||||
flateData.avail_out = kBufferSize;
|
||||
int rc;
|
||||
rc = inflateInit(&flateData);
|
||||
if (rc != Z_OK)
|
||||
return nullptr;
|
||||
|
||||
uint8_t* input = (uint8_t*)src->getMemoryBase();
|
||||
size_t inputLength = src->getLength();
|
||||
if (input == NULL || inputLength == 0) {
|
||||
input = NULL;
|
||||
flateData.next_in = inputBuffer;
|
||||
flateData.avail_in = 0;
|
||||
} else {
|
||||
flateData.next_in = input;
|
||||
flateData.avail_in = SkToUInt(inputLength);
|
||||
}
|
||||
|
||||
rc = Z_OK;
|
||||
while (true) {
|
||||
if (flateData.avail_out < kBufferSize) {
|
||||
if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) {
|
||||
rc = Z_BUF_ERROR;
|
||||
break;
|
||||
}
|
||||
flateData.next_out = outputBuffer;
|
||||
flateData.avail_out = kBufferSize;
|
||||
}
|
||||
if (rc != Z_OK)
|
||||
break;
|
||||
if (flateData.avail_in == 0) {
|
||||
if (input != NULL)
|
||||
break;
|
||||
size_t read = src->read(&inputBuffer, kBufferSize);
|
||||
if (read == 0)
|
||||
break;
|
||||
flateData.next_in = inputBuffer;
|
||||
flateData.avail_in = SkToUInt(read);
|
||||
}
|
||||
rc = inflate(&flateData, Z_NO_FLUSH);
|
||||
}
|
||||
while (rc == Z_OK) {
|
||||
rc = inflate(&flateData, Z_FINISH);
|
||||
if (flateData.avail_out < kBufferSize) {
|
||||
if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out))
|
||||
return nullptr;
|
||||
flateData.next_out = outputBuffer;
|
||||
flateData.avail_out = kBufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
inflateEnd(&flateData);
|
||||
if (rc != Z_STREAM_END) {
|
||||
return nullptr;
|
||||
}
|
||||
return decompressedDynamicMemoryWStream.detachAsStream();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DEF_TEST(SkDeflateWStream, r) {
|
||||
SkRandom random(123456);
|
||||
for (int i = 0; i < 50; ++i) {
|
||||
@ -34,13 +126,7 @@ DEF_TEST(SkDeflateWStream, r) {
|
||||
}
|
||||
SkAutoTDelete<SkStreamAsset> compressed(
|
||||
dynamicMemoryWStream.detachAsStream());
|
||||
|
||||
SkDynamicMemoryWStream decompressedDynamicMemoryWStream;
|
||||
SkAssertResult(SkFlate::Inflate(compressed,
|
||||
&decompressedDynamicMemoryWStream));
|
||||
|
||||
SkAutoTDelete<SkStreamAsset> decompressed(
|
||||
decompressedDynamicMemoryWStream.detachAsStream());
|
||||
SkAutoTDelete<SkStreamAsset> decompressed(stream_inflate(compressed));
|
||||
|
||||
if (decompressed->getLength() != size) {
|
||||
ERRORF(r, "Decompression failed to get right size [%d]."
|
||||
|
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkData.h"
|
||||
#include "SkDeflate.h"
|
||||
#include "SkStream.h"
|
||||
#include "Test.h"
|
||||
|
||||
// A memory stream that reports zero size with the standard call, like
|
||||
// an unseekable file stream would.
|
||||
class SkZeroSizeMemStream : public SkMemoryStream {
|
||||
public:
|
||||
virtual size_t read(void* buffer, size_t size) {
|
||||
if (buffer == NULL && size == 0)
|
||||
return 0;
|
||||
if (buffer == NULL && size == kGetSizeKey)
|
||||
size = 0;
|
||||
return SkMemoryStream::read(buffer, size);
|
||||
}
|
||||
|
||||
static const size_t kGetSizeKey = 0xDEADBEEF;
|
||||
};
|
||||
|
||||
// Returns a deterministic data of the given size that should be
|
||||
// very compressible.
|
||||
static SkData* new_test_data(size_t dataSize) {
|
||||
SkAutoTMalloc<uint8_t> testBuffer(dataSize);
|
||||
for (size_t i = 0; i < dataSize; ++i) {
|
||||
testBuffer[SkToInt(i)] = i % 64;
|
||||
}
|
||||
return SkData::NewFromMalloc(testBuffer.detach(), dataSize);
|
||||
}
|
||||
|
||||
static void TestFlate(skiatest::Reporter* reporter, SkMemoryStream* testStream,
|
||||
size_t dataSize) {
|
||||
SkASSERT(testStream != NULL);
|
||||
|
||||
SkAutoDataUnref testData(new_test_data(dataSize));
|
||||
SkASSERT(testData->size() == dataSize);
|
||||
|
||||
testStream->setMemory(testData->data(), dataSize, /*copyData=*/ true);
|
||||
SkDynamicMemoryWStream compressed;
|
||||
bool deflateSuccess = SkFlate::Deflate(testStream, &compressed);
|
||||
REPORTER_ASSERT(reporter, deflateSuccess);
|
||||
|
||||
// Check that the input data wasn't changed.
|
||||
size_t inputSize = testStream->getLength();
|
||||
if (inputSize == 0) {
|
||||
inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey);
|
||||
}
|
||||
REPORTER_ASSERT(reporter, dataSize == inputSize);
|
||||
if (dataSize == inputSize) {
|
||||
REPORTER_ASSERT(reporter, memcmp(testData->data(),
|
||||
testStream->getMemoryBase(),
|
||||
dataSize) == 0);
|
||||
}
|
||||
|
||||
size_t compressedSize = compressed.getOffset();
|
||||
|
||||
SkAutoDataUnref compressedData(compressed.copyToData());
|
||||
testStream->setData(compressedData.get());
|
||||
|
||||
SkDynamicMemoryWStream uncompressed;
|
||||
bool inflateSuccess = SkFlate::Inflate(testStream, &uncompressed);
|
||||
REPORTER_ASSERT(reporter, inflateSuccess);
|
||||
|
||||
// Check that the input data wasn't changed.
|
||||
inputSize = testStream->getLength();
|
||||
if (inputSize == 0) {
|
||||
inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey);
|
||||
}
|
||||
REPORTER_ASSERT(reporter, compressedSize == inputSize);
|
||||
if (compressedData->size() == inputSize) {
|
||||
REPORTER_ASSERT(reporter, memcmp(testStream->getMemoryBase(),
|
||||
compressedData->data(),
|
||||
compressedData->size()) == 0);
|
||||
}
|
||||
|
||||
// Check that the uncompressed data matches the source data.
|
||||
SkAutoDataUnref uncompressedData(uncompressed.copyToData());
|
||||
REPORTER_ASSERT(reporter, dataSize == uncompressedData->size());
|
||||
if (dataSize == uncompressedData->size()) {
|
||||
REPORTER_ASSERT(reporter, memcmp(testData->data(),
|
||||
uncompressedData->data(),
|
||||
dataSize) == 0);
|
||||
}
|
||||
|
||||
if (compressedSize < 1) { return; }
|
||||
|
||||
double compressionRatio = static_cast<double>(dataSize) / compressedSize;
|
||||
// Assert that some compression took place.
|
||||
REPORTER_ASSERT(reporter, compressionRatio > 1.2);
|
||||
|
||||
if (reporter->verbose()) {
|
||||
SkDebugf("Flate Test: \t input size: " SK_SIZE_T_SPECIFIER
|
||||
"\tcompressed size: " SK_SIZE_T_SPECIFIER
|
||||
"\tratio: %.4g\n",
|
||||
dataSize, compressedSize, compressionRatio);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(Flate, reporter) {
|
||||
SkMemoryStream memStream;
|
||||
TestFlate(reporter, &memStream, 512);
|
||||
TestFlate(reporter, &memStream, 10240);
|
||||
|
||||
SkZeroSizeMemStream fileStream;
|
||||
TestFlate(reporter, &fileStream, 512);
|
||||
TestFlate(reporter, &fileStream, 10240);
|
||||
}
|
@ -99,12 +99,14 @@ static void TestPDFStream(skiatest::Reporter* reporter) {
|
||||
SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get()));
|
||||
|
||||
SkDynamicMemoryWStream compressedByteStream;
|
||||
SkFlate::Deflate(streamData2.get(), &compressedByteStream);
|
||||
SkAutoDataUnref compressedData(compressedByteStream.copyToData());
|
||||
SkDeflateWStream deflateWStream(&compressedByteStream);
|
||||
deflateWStream.write(streamBytes2, strlen(streamBytes2));
|
||||
deflateWStream.finalize();
|
||||
|
||||
SkDynamicMemoryWStream expected;
|
||||
expected.writeText("<</Filter /FlateDecode\n/Length 116>> stream\n");
|
||||
expected.write(compressedData->data(), compressedData->size());
|
||||
compressedByteStream.writeToStream(&expected);
|
||||
compressedByteStream.reset();
|
||||
expected.writeText("\nendstream");
|
||||
SkAutoDataUnref expectedResultData2(expected.copyToData());
|
||||
SkString result = emit_to_string(*stream);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "SkOSFile.h"
|
||||
#include "SkRandom.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkStreamPriv.h"
|
||||
#include "Test.h"
|
||||
|
||||
#ifndef SK_BUILD_FOR_WIN
|
||||
@ -335,3 +336,60 @@ DEF_TEST(StreamPeek_BlockMemoryStream, rep) {
|
||||
}
|
||||
stream_peek_test(rep, asset, expected);
|
||||
}
|
||||
|
||||
namespace {
|
||||
class DumbStream : public SkStream {
|
||||
public:
|
||||
DumbStream(const uint8_t* data, size_t n)
|
||||
: fData(data), fCount(n), fIdx(0) {}
|
||||
size_t read(void* buffer, size_t size) override {
|
||||
size_t c = SkTMin(fCount - fIdx, size);
|
||||
if (c) {
|
||||
memcpy(buffer, &fData[fIdx], size);
|
||||
fIdx += c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
bool isAtEnd() const override {
|
||||
return fCount > fIdx;
|
||||
}
|
||||
private:
|
||||
const uint8_t* fData;
|
||||
size_t fCount, fIdx;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static void stream_copy_test(skiatest::Reporter* reporter,
|
||||
const void* srcData,
|
||||
size_t N,
|
||||
SkStream* stream) {
|
||||
SkDynamicMemoryWStream tgt;
|
||||
if (!SkStreamCopy(&tgt, stream)) {
|
||||
ERRORF(reporter, "SkStreamCopy failed");
|
||||
return;
|
||||
}
|
||||
SkAutoTUnref<SkData> data(tgt.copyToData());
|
||||
tgt.reset();
|
||||
if (data->size() != N) {
|
||||
ERRORF(reporter, "SkStreamCopy incorrect size");
|
||||
return;
|
||||
}
|
||||
if (0 != memcmp(data->data(), srcData, N)) {
|
||||
ERRORF(reporter, "SkStreamCopy bad copy");
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(StreamCopy, reporter) {
|
||||
SkRandom random(123456);
|
||||
static const size_t N = 10000;
|
||||
uint8_t src[N];
|
||||
for (size_t j = 0; j < N; ++j) {
|
||||
src[j] = random.nextU() & 0xff;
|
||||
}
|
||||
// SkStreamCopy had two code paths; this test both.
|
||||
DumbStream dumbStream(src, N);
|
||||
stream_copy_test(reporter, src, N, &dumbStream);
|
||||
SkMemoryStream smartStream(src, N);
|
||||
stream_copy_test(reporter, src, N, &smartStream);
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user