995b467563
Bug: 932080 Bug: b/142252770 An ICO file has a directory of images that are stored later in the file. The directory contains the offset and size of the images. SkIcoCodec uses these to create embedded SkPng/SkBmpCodecs. The old implementation allocated a block of memory for each image and copied the stream into those blocks so that the embedded SkCodecs could independently read their encoded data. Although SkIcoCodec checks for null, this still allows large (albeit temporary - since we'll discard them if the stream does not contain enough data to fill them) allocations and the potential for over- commit. Instead, read the entire stream into a contiguous buffer. If the stream is already actually a buffer, just use that directly. In this case, the new code will do less work. Otherwise, the memory we allocate is limited by the size of the stream. Note that this is a behavior change for a stream that contains two consecutive ICOs, where the client expects to be able to read the second one later. This was an issue for PNGs on Android (b/34073812), but I suspect no one is relying on this behavior for ICO. Update Codec_end test to remove the ICO test. Alternatives to consider: - only buffer the individual encoded images. This will allow us to continue passing the Codec_end test. - lazily read the embedded streams. Currently we read their start to verify they are valid images (at least in the header) and read their actual sizes and bit-depths, which could differ from that listed in the directory. We use those to make a guess at the "best" image to use. An image with mismatched sizes may now decode differently. Change-Id: I30e5f6c8c2e5a0fa135348f61efe151a7f5d4756 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/277058 Auto-Submit: Leon Scroggins <scroggo@google.com> Commit-Queue: Derek Sollenberger <djsollen@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com>
94 lines
3.0 KiB
C++
94 lines
3.0 KiB
C++
/*
|
|
* Copyright 2017 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "include/codec/SkCodec.h"
|
|
#include "include/core/SkBitmap.h"
|
|
#include "include/core/SkData.h"
|
|
#include "include/core/SkImageInfo.h"
|
|
#include "include/core/SkRefCnt.h"
|
|
#include "include/core/SkStream.h"
|
|
#include "include/private/SkTemplates.h"
|
|
#include "tests/Test.h"
|
|
#include "tools/Resources.h"
|
|
|
|
#include <cstring>
|
|
#include <initializer_list>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
namespace {
|
|
// This class wraps another SkStream. It does not own the underlying stream, so
|
|
// that the underlying stream can be reused starting from where the first
|
|
// client left off. This mimics Android's JavaInputStreamAdaptor.
|
|
class UnowningStream : public SkStream {
|
|
public:
|
|
explicit UnowningStream(SkStream* stream)
|
|
: fStream(stream)
|
|
{}
|
|
|
|
size_t read(void* buf, size_t bytes) override {
|
|
return fStream->read(buf, bytes);
|
|
}
|
|
|
|
bool rewind() override {
|
|
return fStream->rewind();
|
|
}
|
|
|
|
bool isAtEnd() const override {
|
|
return fStream->isAtEnd();
|
|
}
|
|
private:
|
|
SkStream* fStream; // Unowned.
|
|
};
|
|
} // namespace
|
|
|
|
// Test that some SkCodecs do not attempt to read input beyond the logical
|
|
// end of the data. Some other SkCodecs do, but some Android apps rely on not
|
|
// doing so for PNGs. Test on other formats that work.
|
|
DEF_TEST(Codec_end, r) {
|
|
for (const char* path : { "images/plane.png",
|
|
"images/yellow_rose.png",
|
|
"images/plane_interlaced.png",
|
|
"images/mandrill.wbmp",
|
|
"images/randPixels.bmp",
|
|
}) {
|
|
sk_sp<SkData> data = GetResourceAsData(path);
|
|
if (!data) {
|
|
continue;
|
|
}
|
|
|
|
const int kNumImages = 2;
|
|
const size_t size = data->size();
|
|
sk_sp<SkData> multiData = SkData::MakeUninitialized(size * kNumImages);
|
|
void* dst = multiData->writable_data();
|
|
for (int i = 0; i < kNumImages; i++) {
|
|
memcpy(SkTAddOffset<void>(dst, size * i), data->data(), size);
|
|
}
|
|
data.reset();
|
|
|
|
SkMemoryStream stream(std::move(multiData));
|
|
for (int i = 0; i < kNumImages; ++i) {
|
|
std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
|
|
std::make_unique<UnowningStream>(&stream)));
|
|
if (!codec) {
|
|
ERRORF(r, "Failed to create a codec from %s, iteration %i", path, i);
|
|
continue;
|
|
}
|
|
|
|
auto info = codec->getInfo().makeColorType(kN32_SkColorType);
|
|
SkBitmap bm;
|
|
bm.allocPixels(info);
|
|
|
|
auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes());
|
|
if (result != SkCodec::kSuccess) {
|
|
ERRORF(r, "Failed to getPixels from %s, iteration %i error %i", path, i, result);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|