2015-06-22 19:48:26 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2015 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2016-01-30 18:01:40 +00:00
|
|
|
#include <functional>
|
2016-03-10 02:44:43 +00:00
|
|
|
#include <initializer_list>
|
2016-06-13 18:18:14 +00:00
|
|
|
#include <vector>
|
2016-01-30 18:01:40 +00:00
|
|
|
|
2016-03-17 13:58:39 +00:00
|
|
|
#include "SkAutoPixmapStorage.h"
|
2015-09-28 18:55:28 +00:00
|
|
|
#include "SkBitmap.h"
|
2015-06-22 19:48:26 +00:00
|
|
|
#include "SkCanvas.h"
|
|
|
|
#include "SkData.h"
|
|
|
|
#include "SkImageEncoder.h"
|
2015-11-24 15:39:40 +00:00
|
|
|
#include "SkImageGenerator.h"
|
2015-06-22 19:48:26 +00:00
|
|
|
#include "SkImage_Base.h"
|
2016-08-01 18:12:58 +00:00
|
|
|
#include "SkImagePriv.h"
|
2015-09-04 18:36:39 +00:00
|
|
|
#include "SkPicture.h"
|
|
|
|
#include "SkPictureRecorder.h"
|
2015-09-03 14:17:25 +00:00
|
|
|
#include "SkPixelSerializer.h"
|
2015-06-22 19:48:26 +00:00
|
|
|
#include "SkRRect.h"
|
2015-09-04 18:36:39 +00:00
|
|
|
#include "SkStream.h"
|
2015-06-22 19:48:26 +00:00
|
|
|
#include "SkSurface.h"
|
|
|
|
#include "SkUtils.h"
|
|
|
|
#include "Test.h"
|
|
|
|
|
2016-11-23 15:55:18 +00:00
|
|
|
#include "sk_tool_utils.h"
|
|
|
|
|
2016-07-22 14:22:04 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
|
|
|
#include "GrGpu.h"
|
|
|
|
#endif
|
|
|
|
|
2016-04-05 19:59:06 +00:00
|
|
|
using namespace sk_gpu_test;
|
2016-03-31 01:56:19 +00:00
|
|
|
|
2015-06-22 19:48:26 +00:00
|
|
|
static void assert_equal(skiatest::Reporter* reporter, SkImage* a, const SkIRect* subsetA,
|
|
|
|
SkImage* b) {
|
|
|
|
const int widthA = subsetA ? subsetA->width() : a->width();
|
|
|
|
const int heightA = subsetA ? subsetA->height() : a->height();
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, widthA == b->width());
|
|
|
|
REPORTER_ASSERT(reporter, heightA == b->height());
|
2016-03-09 23:21:32 +00:00
|
|
|
|
|
|
|
// see https://bug.skia.org/3965
|
|
|
|
//REPORTER_ASSERT(reporter, a->isOpaque() == b->isOpaque());
|
2015-06-22 19:48:26 +00:00
|
|
|
|
2017-01-05 21:40:16 +00:00
|
|
|
// The codecs may have given us back F16, we can't read from F16 raster to N32, only S32.
|
|
|
|
SkImageInfo info = SkImageInfo::MakeS32(widthA, heightA, a->alphaType());
|
2015-06-22 19:48:26 +00:00
|
|
|
SkAutoPixmapStorage pmapA, pmapB;
|
|
|
|
pmapA.alloc(info);
|
|
|
|
pmapB.alloc(info);
|
|
|
|
|
|
|
|
const int srcX = subsetA ? subsetA->x() : 0;
|
|
|
|
const int srcY = subsetA ? subsetA->y() : 0;
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, a->readPixels(pmapA, srcX, srcY));
|
|
|
|
REPORTER_ASSERT(reporter, b->readPixels(pmapB, 0, 0));
|
|
|
|
|
|
|
|
const size_t widthBytes = widthA * info.bytesPerPixel();
|
|
|
|
for (int y = 0; y < heightA; ++y) {
|
|
|
|
REPORTER_ASSERT(reporter, !memcmp(pmapA.addr32(0, y), pmapB.addr32(0, y), widthBytes));
|
|
|
|
}
|
|
|
|
}
|
2015-11-24 15:39:40 +00:00
|
|
|
static void draw_image_test_pattern(SkCanvas* canvas) {
|
2015-06-22 19:48:26 +00:00
|
|
|
canvas->clear(SK_ColorWHITE);
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorBLACK);
|
2015-11-24 15:39:40 +00:00
|
|
|
canvas->drawRect(SkRect::MakeXYWH(5, 5, 10, 10), paint);
|
|
|
|
}
|
2016-03-17 17:51:11 +00:00
|
|
|
static sk_sp<SkImage> create_image() {
|
2015-11-24 15:39:40 +00:00
|
|
|
const SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
|
2016-03-24 01:59:25 +00:00
|
|
|
auto surface(SkSurface::MakeRaster(info));
|
2015-11-24 15:39:40 +00:00
|
|
|
draw_image_test_pattern(surface->getCanvas());
|
2016-03-17 17:51:11 +00:00
|
|
|
return surface->makeImageSnapshot();
|
2015-06-22 19:48:26 +00:00
|
|
|
}
|
2016-08-02 21:40:46 +00:00
|
|
|
static sk_sp<SkData> create_image_data(SkImageInfo* info) {
|
2016-04-20 15:27:18 +00:00
|
|
|
*info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
|
|
|
|
const size_t rowBytes = info->minRowBytes();
|
2016-08-02 21:40:46 +00:00
|
|
|
sk_sp<SkData> data(SkData::MakeUninitialized(rowBytes * info->height()));
|
2016-04-20 15:27:18 +00:00
|
|
|
{
|
|
|
|
SkBitmap bm;
|
|
|
|
bm.installPixels(*info, data->writable_data(), rowBytes);
|
|
|
|
SkCanvas canvas(bm);
|
|
|
|
draw_image_test_pattern(&canvas);
|
|
|
|
}
|
2016-08-02 21:40:46 +00:00
|
|
|
return data;
|
2016-04-20 15:27:18 +00:00
|
|
|
}
|
|
|
|
static sk_sp<SkImage> create_data_image() {
|
|
|
|
SkImageInfo info;
|
|
|
|
sk_sp<SkData> data(create_image_data(&info));
|
2016-08-02 21:40:46 +00:00
|
|
|
return SkImage::MakeRasterData(info, std::move(data), info.minRowBytes());
|
2016-04-20 15:27:18 +00:00
|
|
|
}
|
|
|
|
#if SK_SUPPORT_GPU // not gpu-specific but currently only used in GPU tests
|
2016-03-17 17:51:11 +00:00
|
|
|
static sk_sp<SkImage> create_image_565() {
|
2016-03-10 02:44:43 +00:00
|
|
|
const SkImageInfo info = SkImageInfo::Make(20, 20, kRGB_565_SkColorType, kOpaque_SkAlphaType);
|
2016-03-24 01:59:25 +00:00
|
|
|
auto surface(SkSurface::MakeRaster(info));
|
2016-03-10 02:44:43 +00:00
|
|
|
draw_image_test_pattern(surface->getCanvas());
|
2016-03-17 17:51:11 +00:00
|
|
|
return surface->makeImageSnapshot();
|
2016-03-10 02:44:43 +00:00
|
|
|
}
|
2017-01-03 16:35:56 +00:00
|
|
|
|
|
|
|
static sk_sp<SkImage> create_image_large(int maxTextureSize) {
|
|
|
|
const SkImageInfo info = SkImageInfo::MakeN32(maxTextureSize + 1, 32, kOpaque_SkAlphaType);
|
2016-06-13 19:13:03 +00:00
|
|
|
auto surface(SkSurface::MakeRaster(info));
|
|
|
|
surface->getCanvas()->clear(SK_ColorWHITE);
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorBLACK);
|
|
|
|
surface->getCanvas()->drawRect(SkRect::MakeXYWH(4000, 2, 28000, 30), paint);
|
|
|
|
return surface->makeImageSnapshot();
|
|
|
|
}
|
2016-03-17 17:51:11 +00:00
|
|
|
static sk_sp<SkImage> create_image_ct() {
|
2016-03-10 02:44:43 +00:00
|
|
|
SkPMColor colors[] = {
|
|
|
|
SkPreMultiplyARGB(0xFF, 0xFF, 0xFF, 0x00),
|
|
|
|
SkPreMultiplyARGB(0x80, 0x00, 0xA0, 0xFF),
|
|
|
|
SkPreMultiplyARGB(0xFF, 0xBB, 0x00, 0xBB)
|
|
|
|
};
|
2016-11-04 15:49:42 +00:00
|
|
|
sk_sp<SkColorTable> colorTable(new SkColorTable(colors, SK_ARRAY_COUNT(colors)));
|
2016-03-10 02:44:43 +00:00
|
|
|
uint8_t data[] = {
|
|
|
|
0, 0, 0, 0, 0,
|
|
|
|
0, 1, 1, 1, 0,
|
|
|
|
0, 1, 2, 1, 0,
|
|
|
|
0, 1, 1, 1, 0,
|
|
|
|
0, 0, 0, 0, 0
|
|
|
|
};
|
|
|
|
SkImageInfo info = SkImageInfo::Make(5, 5, kIndex_8_SkColorType, kPremul_SkAlphaType);
|
2016-11-04 15:49:42 +00:00
|
|
|
return SkImage::MakeRasterCopy(SkPixmap(info, data, 5, colorTable.get()));
|
2016-03-10 02:44:43 +00:00
|
|
|
}
|
2016-03-17 17:51:11 +00:00
|
|
|
static sk_sp<SkImage> create_picture_image() {
|
2016-01-30 18:01:40 +00:00
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* canvas = recorder.beginRecording(10, 10);
|
|
|
|
canvas->clear(SK_ColorCYAN);
|
2016-03-18 14:25:55 +00:00
|
|
|
return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(), SkISize::Make(10, 10),
|
2016-12-16 16:55:18 +00:00
|
|
|
nullptr, nullptr,
|
|
|
|
SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named));
|
2016-01-30 18:01:40 +00:00
|
|
|
};
|
|
|
|
#endif
|
2015-11-24 15:39:40 +00:00
|
|
|
// Want to ensure that our Release is called when the owning image is destroyed
|
|
|
|
struct RasterDataHolder {
|
|
|
|
RasterDataHolder() : fReleaseCount(0) {}
|
2016-08-02 21:40:46 +00:00
|
|
|
sk_sp<SkData> fData;
|
2015-11-24 15:39:40 +00:00
|
|
|
int fReleaseCount;
|
|
|
|
static void Release(const void* pixels, void* context) {
|
|
|
|
RasterDataHolder* self = static_cast<RasterDataHolder*>(context);
|
|
|
|
self->fReleaseCount++;
|
|
|
|
self->fData.reset();
|
|
|
|
}
|
|
|
|
};
|
2016-03-17 17:51:11 +00:00
|
|
|
static sk_sp<SkImage> create_rasterproc_image(RasterDataHolder* dataHolder) {
|
2015-11-24 15:39:40 +00:00
|
|
|
SkASSERT(dataHolder);
|
|
|
|
SkImageInfo info;
|
2016-08-02 21:40:46 +00:00
|
|
|
dataHolder->fData = create_image_data(&info);
|
|
|
|
return SkImage::MakeFromRaster(SkPixmap(info, dataHolder->fData->data(), info.minRowBytes()),
|
2016-03-17 17:51:11 +00:00
|
|
|
RasterDataHolder::Release, dataHolder);
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
2016-03-17 17:51:11 +00:00
|
|
|
static sk_sp<SkImage> create_codec_image() {
|
2015-11-24 15:39:40 +00:00
|
|
|
SkImageInfo info;
|
2016-08-02 21:40:46 +00:00
|
|
|
sk_sp<SkData> data(create_image_data(&info));
|
2015-11-24 15:39:40 +00:00
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.installPixels(info, data->writable_data(), info.minRowBytes());
|
2016-11-23 15:55:18 +00:00
|
|
|
sk_sp<SkData> src(sk_tool_utils::EncodeImageToData(bitmap, SkEncodedImageFormat::kPNG, 100));
|
2016-08-02 21:40:46 +00:00
|
|
|
return SkImage::MakeFromEncoded(std::move(src));
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
|
|
|
#if SK_SUPPORT_GPU
|
2016-03-17 17:51:11 +00:00
|
|
|
static sk_sp<SkImage> create_gpu_image(GrContext* context) {
|
2015-11-24 15:39:40 +00:00
|
|
|
const SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
|
2016-03-24 01:59:25 +00:00
|
|
|
auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
|
2015-11-24 15:39:40 +00:00
|
|
|
draw_image_test_pattern(surface->getCanvas());
|
2016-03-17 17:51:11 +00:00
|
|
|
return surface->makeImageSnapshot();
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
|
|
|
#endif
|
2015-06-22 19:48:26 +00:00
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
static void test_encode(skiatest::Reporter* reporter, SkImage* image) {
|
2015-06-22 19:48:26 +00:00
|
|
|
const SkIRect ir = SkIRect::MakeXYWH(5, 5, 10, 10);
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkData> origEncoded(image->encode());
|
2015-06-22 19:48:26 +00:00
|
|
|
REPORTER_ASSERT(reporter, origEncoded);
|
|
|
|
REPORTER_ASSERT(reporter, origEncoded->size() > 0);
|
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> decoded(SkImage::MakeFromEncoded(origEncoded));
|
Make SkPngCodec decode progressively.
This is a step towards using SkCodec in Chromium, where progressive
decoding is necessary.
Switch from using png_read_row (which expects all the data to be
available) to png_process_data, which uses callbacks when rows are
available.
Create a new API for SkCodec, which supports progressive decoding and
scanline decoding. Future changes will switch the other clients off of
startScanlineDecode and get/skip-Scanlines to the new API.
Remove SkCodec::kNone_ScanlineOrder, which was only used for interlaced
PNG images. In the new API, interlaced PNG fits kTopDown. Also remove
updateCurrScanline(), which was only used by the old implementation for
interlaced PNG.
DMSrcSink:
- In CodecSrc::kScanline_Mode, use the new method for scanline decoding
for the supported formats (just PNG and PNG-in-ICO for now).
fuzz.cpp:
- Remove reference to kNone_ScanlineOrder
SkCodec:
- Add new APIs:
- startIncrementalDecode
- incrementalDecode
- Remove kNone_SkScanlineOrder and updateCurrScanline()
- Set fDstInfo and fOptions in getPixels(). This may not be necessary
for all implementations, but it simplifies things for SkPngCodec.
SkPngCodec:
- Implement new APIs
- Switch from sk_read_fn/png_read_row etc to png_process_data
- Expand AutoCleanPng's role to decode the header and create the
SkPngCodec
- Make the interlaced PNG decoder report how many lines were
initialized during an incomplete decode
SkIcoCodec:
- Implement the new APIs; supported for PNG in ICO
SkSampledCodec:
- Call the new method for decoding scanlines, and fall back to the old
method if the new version is unimplemented
- Remove references to kNone_SkScanlineOrder
tests/CodecPartial:
- Add a test which decodes part of an image, then finishes the decode,
and compares it to the straightforward method
tests/CodecTest:
- Add a test which decodes all scanlines using the new method
- Repurpose the Codec_stripes test to decode using the new method in
sections rather than all at once
- In the method check(), add a parameter for whether the image supports
the new method of scanline decoding, and be explicit about whether an
image supports incomplete
- Test incomplete PNG decodes. We should have been doing it anyway for
non-interlaced (except for an image that is too small - one row), but
the new method supports interlaced incomplete as well
- Make test_invalid_parameters test the new method
- Add a test to ensure that it's safe to fall back to scanline decoding without
rewinding
BUG=skia:4211
The new version was generally faster than the old version (but not significantly so).
Some raw performance differences can be found at https://docs.google.com/a/google.com/spreadsheets/d/1Gis3aRCEa72qBNDRMgGDg3jD-pMgO-FXldlNF9ejo4o/
Design doc can be found at https://docs.google.com/a/google.com/document/d/11Mn8-ePDKwVEMCjs3nWwSjxcSpJ_Cu8DF57KNtUmgLM/
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1997703003
Review-Url: https://codereview.chromium.org/1997703003
2016-09-16 15:20:38 +00:00
|
|
|
if (!decoded) {
|
|
|
|
ERRORF(reporter, "failed to decode image!");
|
|
|
|
return;
|
|
|
|
}
|
2015-06-22 19:48:26 +00:00
|
|
|
REPORTER_ASSERT(reporter, decoded);
|
2016-03-17 17:51:11 +00:00
|
|
|
assert_equal(reporter, image, nullptr, decoded.get());
|
2015-06-22 19:48:26 +00:00
|
|
|
|
|
|
|
// Now see if we can instantiate an image from a subset of the surface/origEncoded
|
2016-03-16 17:28:35 +00:00
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
decoded = SkImage::MakeFromEncoded(origEncoded, &ir);
|
2015-06-22 19:48:26 +00:00
|
|
|
REPORTER_ASSERT(reporter, decoded);
|
2016-03-17 17:51:11 +00:00
|
|
|
assert_equal(reporter, image, &ir, decoded.get());
|
2015-06-22 19:48:26 +00:00
|
|
|
}
|
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
DEF_TEST(ImageEncode, reporter) {
|
2016-03-17 17:51:11 +00:00
|
|
|
test_encode(reporter, create_image().get());
|
2015-06-22 19:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#if SK_SUPPORT_GPU
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageEncode_Gpu, reporter, ctxInfo) {
|
2016-05-11 13:33:06 +00:00
|
|
|
test_encode(reporter, create_gpu_image(ctxInfo.grContext()).get());
|
2015-06-22 19:48:26 +00:00
|
|
|
}
|
|
|
|
#endif
|
2015-07-04 04:01:10 +00:00
|
|
|
|
2016-08-01 18:12:58 +00:00
|
|
|
DEF_TEST(Image_MakeFromRasterBitmap, reporter) {
|
|
|
|
const struct {
|
2016-08-05 19:07:41 +00:00
|
|
|
SkCopyPixelsMode fCPM;
|
|
|
|
bool fExpectSameAsMutable;
|
|
|
|
bool fExpectSameAsImmutable;
|
2016-08-01 18:12:58 +00:00
|
|
|
} recs[] = {
|
2016-08-05 19:07:41 +00:00
|
|
|
{ kIfMutable_SkCopyPixelsMode, false, true },
|
|
|
|
{ kAlways_SkCopyPixelsMode, false, false },
|
|
|
|
{ kNever_SkCopyPixelsMode, true, true },
|
2016-08-01 18:12:58 +00:00
|
|
|
};
|
|
|
|
for (auto rec : recs) {
|
|
|
|
SkPixmap pm;
|
|
|
|
SkBitmap bm;
|
|
|
|
bm.allocN32Pixels(100, 100);
|
|
|
|
|
2016-08-05 19:07:41 +00:00
|
|
|
auto img = SkMakeImageFromRasterBitmap(bm, rec.fCPM);
|
2016-08-01 18:12:58 +00:00
|
|
|
REPORTER_ASSERT(reporter, img->peekPixels(&pm));
|
|
|
|
const bool sameMutable = pm.addr32(0, 0) == bm.getAddr32(0, 0);
|
|
|
|
REPORTER_ASSERT(reporter, rec.fExpectSameAsMutable == sameMutable);
|
2016-08-05 20:19:01 +00:00
|
|
|
REPORTER_ASSERT(reporter, (bm.getGenerationID() == img->uniqueID()) == sameMutable);
|
2016-08-01 18:12:58 +00:00
|
|
|
|
|
|
|
bm.notifyPixelsChanged(); // force a new generation ID
|
|
|
|
|
|
|
|
bm.setImmutable();
|
2016-08-05 19:07:41 +00:00
|
|
|
img = SkMakeImageFromRasterBitmap(bm, rec.fCPM);
|
2016-08-01 18:12:58 +00:00
|
|
|
REPORTER_ASSERT(reporter, img->peekPixels(&pm));
|
|
|
|
const bool sameImmutable = pm.addr32(0, 0) == bm.getAddr32(0, 0);
|
|
|
|
REPORTER_ASSERT(reporter, rec.fExpectSameAsImmutable == sameImmutable);
|
2016-08-05 20:19:01 +00:00
|
|
|
REPORTER_ASSERT(reporter, (bm.getGenerationID() == img->uniqueID()) == sameImmutable);
|
2016-08-01 18:12:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-03 14:17:25 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
const char* kSerializedData = "serialized";
|
|
|
|
|
|
|
|
class MockSerializer : public SkPixelSerializer {
|
2015-09-04 18:36:39 +00:00
|
|
|
public:
|
2016-08-02 21:40:46 +00:00
|
|
|
MockSerializer(sk_sp<SkData> (*func)()) : fFunc(func), fDidEncode(false) { }
|
2015-09-04 18:36:39 +00:00
|
|
|
|
|
|
|
bool didEncode() const { return fDidEncode; }
|
|
|
|
|
2015-09-03 14:17:25 +00:00
|
|
|
protected:
|
2015-09-28 16:58:41 +00:00
|
|
|
bool onUseEncodedData(const void*, size_t) override {
|
|
|
|
return false;
|
2015-09-03 14:17:25 +00:00
|
|
|
}
|
|
|
|
|
2015-12-10 17:30:57 +00:00
|
|
|
SkData* onEncode(const SkPixmap&) override {
|
2015-09-04 18:36:39 +00:00
|
|
|
fDidEncode = true;
|
2016-08-02 21:40:46 +00:00
|
|
|
return fFunc().release();
|
2015-09-03 14:17:25 +00:00
|
|
|
}
|
2015-09-04 18:36:39 +00:00
|
|
|
|
|
|
|
private:
|
2016-08-02 21:40:46 +00:00
|
|
|
sk_sp<SkData> (*fFunc)();
|
2015-09-04 18:36:39 +00:00
|
|
|
bool fDidEncode;
|
|
|
|
|
|
|
|
typedef SkPixelSerializer INHERITED;
|
2015-09-03 14:17:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
// Test that SkImage encoding observes custom pixel serializers.
|
|
|
|
DEF_TEST(Image_Encode_Serializer, reporter) {
|
2016-08-02 21:40:46 +00:00
|
|
|
MockSerializer serializer([]() -> sk_sp<SkData> {
|
|
|
|
return SkData::MakeWithCString(kSerializedData);
|
|
|
|
});
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image(create_image());
|
2016-08-02 21:40:46 +00:00
|
|
|
sk_sp<SkData> encoded(image->encode(&serializer));
|
|
|
|
sk_sp<SkData> reference(SkData::MakeWithCString(kSerializedData));
|
2015-09-03 14:17:25 +00:00
|
|
|
|
2015-09-04 18:36:39 +00:00
|
|
|
REPORTER_ASSERT(reporter, serializer.didEncode());
|
2015-09-03 14:17:25 +00:00
|
|
|
REPORTER_ASSERT(reporter, encoded);
|
|
|
|
REPORTER_ASSERT(reporter, encoded->size() > 0);
|
2016-08-02 21:40:46 +00:00
|
|
|
REPORTER_ASSERT(reporter, encoded->equals(reference.get()));
|
2015-09-03 14:17:25 +00:00
|
|
|
}
|
|
|
|
|
2015-09-04 18:36:39 +00:00
|
|
|
// Test that image encoding failures do not break picture serialization/deserialization.
|
|
|
|
DEF_TEST(Image_Serialize_Encoding_Failure, reporter) {
|
2016-03-24 01:59:25 +00:00
|
|
|
auto surface(SkSurface::MakeRasterN32Premul(100, 100));
|
2015-09-04 18:36:39 +00:00
|
|
|
surface->getCanvas()->clear(SK_ColorGREEN);
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image(surface->makeImageSnapshot());
|
2015-09-04 18:36:39 +00:00
|
|
|
REPORTER_ASSERT(reporter, image);
|
|
|
|
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* canvas = recorder.beginRecording(100, 100);
|
|
|
|
canvas->drawImage(image, 0, 0);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
2015-09-04 18:36:39 +00:00
|
|
|
REPORTER_ASSERT(reporter, picture);
|
|
|
|
REPORTER_ASSERT(reporter, picture->approximateOpCount() > 0);
|
|
|
|
|
2016-08-02 21:40:46 +00:00
|
|
|
MockSerializer emptySerializer([]() -> sk_sp<SkData> { return SkData::MakeEmpty(); });
|
|
|
|
MockSerializer nullSerializer([]() -> sk_sp<SkData> { return nullptr; });
|
2015-09-04 18:36:39 +00:00
|
|
|
MockSerializer* serializers[] = { &emptySerializer, &nullSerializer };
|
|
|
|
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(serializers); ++i) {
|
|
|
|
SkDynamicMemoryWStream wstream;
|
|
|
|
REPORTER_ASSERT(reporter, !serializers[i]->didEncode());
|
|
|
|
picture->serialize(&wstream, serializers[i]);
|
|
|
|
REPORTER_ASSERT(reporter, serializers[i]->didEncode());
|
|
|
|
|
2016-11-03 18:40:50 +00:00
|
|
|
std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
|
|
|
|
sk_sp<SkPicture> deserialized(SkPicture::MakeFromStream(rstream.get()));
|
2015-09-04 18:36:39 +00:00
|
|
|
REPORTER_ASSERT(reporter, deserialized);
|
|
|
|
REPORTER_ASSERT(reporter, deserialized->approximateOpCount() > 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-04 04:01:10 +00:00
|
|
|
DEF_TEST(Image_NewRasterCopy, reporter) {
|
|
|
|
const SkPMColor red = SkPackARGB32(0xFF, 0xFF, 0, 0);
|
|
|
|
const SkPMColor green = SkPackARGB32(0xFF, 0, 0xFF, 0);
|
|
|
|
const SkPMColor blue = SkPackARGB32(0xFF, 0, 0, 0xFF);
|
|
|
|
SkPMColor colors[] = { red, green, blue, 0 };
|
2016-11-04 15:49:42 +00:00
|
|
|
sk_sp<SkColorTable> ctable(new SkColorTable(colors, SK_ARRAY_COUNT(colors)));
|
2015-07-04 04:01:10 +00:00
|
|
|
// The colortable made a copy, so we can trash the original colors
|
|
|
|
memset(colors, 0xFF, sizeof(colors));
|
|
|
|
|
|
|
|
const SkImageInfo srcInfo = SkImageInfo::Make(2, 2, kIndex_8_SkColorType, kPremul_SkAlphaType);
|
|
|
|
const size_t srcRowBytes = 2 * sizeof(uint8_t);
|
|
|
|
uint8_t indices[] = { 0, 1, 2, 3 };
|
2016-11-04 15:49:42 +00:00
|
|
|
auto image = SkImage::MakeRasterCopy(SkPixmap(srcInfo, indices, srcRowBytes, ctable.get()));
|
2015-07-04 04:01:10 +00:00
|
|
|
// The image made a copy, so we can trash the original indices
|
|
|
|
memset(indices, 0xFF, sizeof(indices));
|
|
|
|
|
|
|
|
const SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(2, 2);
|
|
|
|
const size_t dstRowBytes = 2 * sizeof(SkPMColor);
|
|
|
|
SkPMColor pixels[4];
|
|
|
|
memset(pixels, 0xFF, sizeof(pixels)); // init with values we don't expect
|
|
|
|
image->readPixels(dstInfo, pixels, dstRowBytes, 0, 0);
|
|
|
|
REPORTER_ASSERT(reporter, red == pixels[0]);
|
|
|
|
REPORTER_ASSERT(reporter, green == pixels[1]);
|
|
|
|
REPORTER_ASSERT(reporter, blue == pixels[2]);
|
|
|
|
REPORTER_ASSERT(reporter, 0 == pixels[3]);
|
|
|
|
}
|
2015-07-22 12:56:16 +00:00
|
|
|
|
|
|
|
// Test that a draw that only partially covers the drawing surface isn't
|
|
|
|
// interpreted as covering the entire drawing surface (i.e., exercise one of the
|
|
|
|
// conditions of SkCanvas::wouldOverwriteEntireSurface()).
|
|
|
|
DEF_TEST(Image_RetainSnapshot, reporter) {
|
|
|
|
const SkPMColor red = SkPackARGB32(0xFF, 0xFF, 0, 0);
|
|
|
|
const SkPMColor green = SkPackARGB32(0xFF, 0, 0xFF, 0);
|
|
|
|
SkImageInfo info = SkImageInfo::MakeN32Premul(2, 2);
|
2016-03-24 01:59:25 +00:00
|
|
|
auto surface(SkSurface::MakeRaster(info));
|
2015-07-22 12:56:16 +00:00
|
|
|
surface->getCanvas()->clear(0xFF00FF00);
|
|
|
|
|
|
|
|
SkPMColor pixels[4];
|
|
|
|
memset(pixels, 0xFF, sizeof(pixels)); // init with values we don't expect
|
|
|
|
const SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(2, 2);
|
|
|
|
const size_t dstRowBytes = 2 * sizeof(SkPMColor);
|
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image1(surface->makeImageSnapshot());
|
2015-07-22 12:56:16 +00:00
|
|
|
REPORTER_ASSERT(reporter, image1->readPixels(dstInfo, pixels, dstRowBytes, 0, 0));
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(pixels); ++i) {
|
|
|
|
REPORTER_ASSERT(reporter, pixels[i] == green);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPaint paint;
|
2016-10-06 00:33:02 +00:00
|
|
|
paint.setBlendMode(SkBlendMode::kSrc);
|
2015-07-22 12:56:16 +00:00
|
|
|
paint.setColor(SK_ColorRED);
|
|
|
|
|
|
|
|
surface->getCanvas()->drawRect(SkRect::MakeXYWH(1, 1, 1, 1), paint);
|
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image2(surface->makeImageSnapshot());
|
2015-07-22 12:56:16 +00:00
|
|
|
REPORTER_ASSERT(reporter, image2->readPixels(dstInfo, pixels, dstRowBytes, 0, 0));
|
|
|
|
REPORTER_ASSERT(reporter, pixels[0] == green);
|
|
|
|
REPORTER_ASSERT(reporter, pixels[1] == green);
|
|
|
|
REPORTER_ASSERT(reporter, pixels[2] == green);
|
|
|
|
REPORTER_ASSERT(reporter, pixels[3] == red);
|
|
|
|
}
|
2015-07-31 01:58:23 +00:00
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static void make_bitmap_mutable(SkBitmap* bm) {
|
|
|
|
bm->allocN32Pixels(10, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void make_bitmap_immutable(SkBitmap* bm) {
|
|
|
|
bm->allocN32Pixels(10, 10);
|
|
|
|
bm->setImmutable();
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(image_newfrombitmap, reporter) {
|
|
|
|
const struct {
|
|
|
|
void (*fMakeProc)(SkBitmap*);
|
|
|
|
bool fExpectPeekSuccess;
|
|
|
|
bool fExpectSharedID;
|
2015-08-20 15:47:26 +00:00
|
|
|
bool fExpectLazy;
|
2015-07-31 01:58:23 +00:00
|
|
|
} rec[] = {
|
2015-08-20 15:47:26 +00:00
|
|
|
{ make_bitmap_mutable, true, false, false },
|
|
|
|
{ make_bitmap_immutable, true, true, false },
|
2015-07-31 01:58:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(rec); ++i) {
|
|
|
|
SkBitmap bm;
|
|
|
|
rec[i].fMakeProc(&bm);
|
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image(SkImage::MakeFromBitmap(bm));
|
2015-07-31 01:58:23 +00:00
|
|
|
SkPixmap pmap;
|
|
|
|
|
|
|
|
const bool sharedID = (image->uniqueID() == bm.getGenerationID());
|
|
|
|
REPORTER_ASSERT(reporter, sharedID == rec[i].fExpectSharedID);
|
|
|
|
|
|
|
|
const bool peekSuccess = image->peekPixels(&pmap);
|
|
|
|
REPORTER_ASSERT(reporter, peekSuccess == rec[i].fExpectPeekSuccess);
|
2015-08-20 15:47:26 +00:00
|
|
|
|
|
|
|
const bool lazy = image->isLazyGenerated();
|
|
|
|
REPORTER_ASSERT(reporter, lazy == rec[i].fExpectLazy);
|
2015-07-31 01:58:23 +00:00
|
|
|
}
|
|
|
|
}
|
2015-08-04 15:10:13 +00:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if SK_SUPPORT_GPU
|
|
|
|
|
|
|
|
#include "SkBitmapCache.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This tests the caching (and preemptive purge) of the raster equivalent of a gpu-image.
|
|
|
|
* We cache it for performance when drawing into a raster surface.
|
|
|
|
*
|
|
|
|
* A cleaner test would know if each drawImage call triggered a read-back from the gpu,
|
|
|
|
* but we don't have that facility (at the moment) so we use a little internal knowledge
|
|
|
|
* of *how* the raster version is cached, and look for that.
|
|
|
|
*/
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(c, reporter, ctxInfo) {
|
2015-11-24 15:39:40 +00:00
|
|
|
SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
|
2016-05-11 13:33:06 +00:00
|
|
|
sk_sp<SkImage> image(create_gpu_image(ctxInfo.grContext()));
|
2015-08-04 15:10:13 +00:00
|
|
|
const uint32_t uniqueID = image->uniqueID();
|
|
|
|
|
2016-03-24 01:59:25 +00:00
|
|
|
auto surface(SkSurface::MakeRaster(info));
|
2015-08-04 15:10:13 +00:00
|
|
|
|
|
|
|
// now we can test drawing a gpu-backed image into a cpu-backed surface
|
|
|
|
|
|
|
|
{
|
|
|
|
SkBitmap cachedBitmap;
|
|
|
|
REPORTER_ASSERT(reporter, !SkBitmapCache::Find(uniqueID, &cachedBitmap));
|
|
|
|
}
|
|
|
|
|
|
|
|
surface->getCanvas()->drawImage(image, 0, 0);
|
|
|
|
{
|
|
|
|
SkBitmap cachedBitmap;
|
|
|
|
if (SkBitmapCache::Find(uniqueID, &cachedBitmap)) {
|
|
|
|
REPORTER_ASSERT(reporter, cachedBitmap.getGenerationID() == uniqueID);
|
|
|
|
REPORTER_ASSERT(reporter, cachedBitmap.isImmutable());
|
|
|
|
REPORTER_ASSERT(reporter, cachedBitmap.getPixels());
|
|
|
|
} else {
|
|
|
|
// unexpected, but not really a bug, since the cache is global and this test may be
|
|
|
|
// run w/ other threads competing for its budget.
|
|
|
|
SkDebugf("SkImage_Gpu2Cpu : cachedBitmap was already purged\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
image.reset(nullptr);
|
|
|
|
{
|
|
|
|
SkBitmap cachedBitmap;
|
|
|
|
REPORTER_ASSERT(reporter, !SkBitmapCache::Find(uniqueID, &cachedBitmap));
|
|
|
|
}
|
|
|
|
}
|
2016-01-30 18:01:40 +00:00
|
|
|
|
2016-07-13 01:11:17 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkImage_makeNonTextureImage, reporter, contextInfo) {
|
|
|
|
GrContext* context = contextInfo.grContext();
|
|
|
|
|
|
|
|
std::function<sk_sp<SkImage>()> imageFactories[] = {
|
|
|
|
create_image,
|
|
|
|
create_codec_image,
|
|
|
|
create_data_image,
|
|
|
|
create_picture_image,
|
|
|
|
[context] { return create_gpu_image(context); },
|
|
|
|
};
|
|
|
|
for (auto factory : imageFactories) {
|
|
|
|
sk_sp<SkImage> image = factory();
|
|
|
|
if (!image->isTextureBacked()) {
|
|
|
|
REPORTER_ASSERT(reporter, image->makeNonTextureImage().get() == image.get());
|
2016-12-05 21:12:48 +00:00
|
|
|
continue;
|
2016-07-13 01:11:17 +00:00
|
|
|
}
|
|
|
|
auto rasterImage = image->makeNonTextureImage();
|
|
|
|
if (!rasterImage) {
|
|
|
|
ERRORF(reporter, "makeNonTextureImage failed for texture-backed image.");
|
|
|
|
}
|
|
|
|
REPORTER_ASSERT(reporter, !rasterImage->isTextureBacked());
|
|
|
|
assert_equal(reporter, image.get(), nullptr, rasterImage.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-15 21:15:30 +00:00
|
|
|
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkImage_drawAbandonedGpuImage, reporter, contextInfo) {
|
2016-06-15 14:36:15 +00:00
|
|
|
auto context = contextInfo.grContext();
|
|
|
|
auto image = create_gpu_image(context);
|
|
|
|
auto info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
|
|
|
|
auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
|
|
|
|
as_IB(image)->peekTexture()->abandon();
|
|
|
|
surface->getCanvas()->drawImage(image, 0, 0);
|
|
|
|
}
|
|
|
|
|
2015-08-04 15:10:13 +00:00
|
|
|
#endif
|
2015-09-28 18:55:28 +00:00
|
|
|
|
2015-11-07 13:29:00 +00:00
|
|
|
// https://bug.skia.org/4390
|
2015-09-28 18:55:28 +00:00
|
|
|
DEF_TEST(ImageFromIndex8Bitmap, r) {
|
|
|
|
SkPMColor pmColors[1] = {SkPreMultiplyColor(SK_ColorWHITE)};
|
|
|
|
SkBitmap bm;
|
2016-11-04 15:49:42 +00:00
|
|
|
sk_sp<SkColorTable> ctable( new SkColorTable(pmColors, SK_ARRAY_COUNT(pmColors)));
|
|
|
|
SkImageInfo info = SkImageInfo::Make(1, 1, kIndex_8_SkColorType, kPremul_SkAlphaType);
|
|
|
|
bm.allocPixels(info, nullptr, ctable.get());
|
2015-09-28 18:55:28 +00:00
|
|
|
SkAutoLockPixels autoLockPixels(bm);
|
|
|
|
*bm.getAddr8(0, 0) = 0;
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> img(SkImage::MakeFromBitmap(bm));
|
|
|
|
REPORTER_ASSERT(r, img != nullptr);
|
2015-09-28 18:55:28 +00:00
|
|
|
}
|
2015-11-18 06:53:28 +00:00
|
|
|
|
|
|
|
class EmptyGenerator : public SkImageGenerator {
|
|
|
|
public:
|
|
|
|
EmptyGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {}
|
|
|
|
};
|
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
DEF_TEST(ImageEmpty, reporter) {
|
2015-11-18 06:53:28 +00:00
|
|
|
const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType);
|
2016-03-17 17:51:11 +00:00
|
|
|
SkPixmap pmap(info, nullptr, 0);
|
|
|
|
REPORTER_ASSERT(reporter, nullptr == SkImage::MakeRasterCopy(pmap));
|
|
|
|
REPORTER_ASSERT(reporter, nullptr == SkImage::MakeRasterData(info, nullptr, 0));
|
|
|
|
REPORTER_ASSERT(reporter, nullptr == SkImage::MakeFromRaster(pmap, nullptr, nullptr));
|
|
|
|
REPORTER_ASSERT(reporter, nullptr == SkImage::MakeFromGenerator(new EmptyGenerator));
|
2015-11-18 06:53:28 +00:00
|
|
|
}
|
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
DEF_TEST(ImageDataRef, reporter) {
|
2015-11-18 06:53:28 +00:00
|
|
|
SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
|
|
|
|
size_t rowBytes = info.minRowBytes();
|
|
|
|
size_t size = info.getSafeSize(rowBytes);
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkData> data = SkData::MakeUninitialized(size);
|
2015-11-18 06:53:28 +00:00
|
|
|
REPORTER_ASSERT(reporter, data->unique());
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image = SkImage::MakeRasterData(info, data, rowBytes);
|
2015-11-18 06:53:28 +00:00
|
|
|
REPORTER_ASSERT(reporter, !data->unique());
|
2016-03-17 17:51:11 +00:00
|
|
|
image.reset();
|
2015-11-18 06:53:28 +00:00
|
|
|
REPORTER_ASSERT(reporter, data->unique());
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool has_pixels(const SkPMColor pixels[], int count, SkPMColor expected) {
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
if (pixels[i] != expected) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
static void test_read_pixels(skiatest::Reporter* reporter, SkImage* image) {
|
Make SkPngCodec decode progressively.
This is a step towards using SkCodec in Chromium, where progressive
decoding is necessary.
Switch from using png_read_row (which expects all the data to be
available) to png_process_data, which uses callbacks when rows are
available.
Create a new API for SkCodec, which supports progressive decoding and
scanline decoding. Future changes will switch the other clients off of
startScanlineDecode and get/skip-Scanlines to the new API.
Remove SkCodec::kNone_ScanlineOrder, which was only used for interlaced
PNG images. In the new API, interlaced PNG fits kTopDown. Also remove
updateCurrScanline(), which was only used by the old implementation for
interlaced PNG.
DMSrcSink:
- In CodecSrc::kScanline_Mode, use the new method for scanline decoding
for the supported formats (just PNG and PNG-in-ICO for now).
fuzz.cpp:
- Remove reference to kNone_ScanlineOrder
SkCodec:
- Add new APIs:
- startIncrementalDecode
- incrementalDecode
- Remove kNone_SkScanlineOrder and updateCurrScanline()
- Set fDstInfo and fOptions in getPixels(). This may not be necessary
for all implementations, but it simplifies things for SkPngCodec.
SkPngCodec:
- Implement new APIs
- Switch from sk_read_fn/png_read_row etc to png_process_data
- Expand AutoCleanPng's role to decode the header and create the
SkPngCodec
- Make the interlaced PNG decoder report how many lines were
initialized during an incomplete decode
SkIcoCodec:
- Implement the new APIs; supported for PNG in ICO
SkSampledCodec:
- Call the new method for decoding scanlines, and fall back to the old
method if the new version is unimplemented
- Remove references to kNone_SkScanlineOrder
tests/CodecPartial:
- Add a test which decodes part of an image, then finishes the decode,
and compares it to the straightforward method
tests/CodecTest:
- Add a test which decodes all scanlines using the new method
- Repurpose the Codec_stripes test to decode using the new method in
sections rather than all at once
- In the method check(), add a parameter for whether the image supports
the new method of scanline decoding, and be explicit about whether an
image supports incomplete
- Test incomplete PNG decodes. We should have been doing it anyway for
non-interlaced (except for an image that is too small - one row), but
the new method supports interlaced incomplete as well
- Make test_invalid_parameters test the new method
- Add a test to ensure that it's safe to fall back to scanline decoding without
rewinding
BUG=skia:4211
The new version was generally faster than the old version (but not significantly so).
Some raw performance differences can be found at https://docs.google.com/a/google.com/spreadsheets/d/1Gis3aRCEa72qBNDRMgGDg3jD-pMgO-FXldlNF9ejo4o/
Design doc can be found at https://docs.google.com/a/google.com/document/d/11Mn8-ePDKwVEMCjs3nWwSjxcSpJ_Cu8DF57KNtUmgLM/
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1997703003
Review-Url: https://codereview.chromium.org/1997703003
2016-09-16 15:20:38 +00:00
|
|
|
if (!image) {
|
|
|
|
ERRORF(reporter, "Failed to create image!");
|
|
|
|
return;
|
|
|
|
}
|
2015-11-24 15:39:40 +00:00
|
|
|
const SkPMColor expected = SkPreMultiplyColor(SK_ColorWHITE);
|
2015-11-18 06:53:28 +00:00
|
|
|
const SkPMColor notExpected = ~expected;
|
|
|
|
|
|
|
|
const int w = 2, h = 2;
|
|
|
|
const size_t rowBytes = w * sizeof(SkPMColor);
|
|
|
|
SkPMColor pixels[w*h];
|
|
|
|
|
|
|
|
SkImageInfo info;
|
|
|
|
|
|
|
|
info = SkImageInfo::MakeUnknown(w, h);
|
|
|
|
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, 0));
|
|
|
|
|
|
|
|
// out-of-bounds should fail
|
|
|
|
info = SkImageInfo::MakeN32Premul(w, h);
|
|
|
|
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, -w, 0));
|
|
|
|
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, -h));
|
|
|
|
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, image->width(), 0));
|
|
|
|
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, image->height()));
|
|
|
|
|
|
|
|
// top-left should succeed
|
2015-11-24 15:39:40 +00:00
|
|
|
sk_memset32(pixels, notExpected, w*h);
|
2015-11-18 06:53:28 +00:00
|
|
|
REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, 0, 0));
|
|
|
|
REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
|
|
|
|
|
|
|
|
// bottom-right should succeed
|
2015-11-24 15:39:40 +00:00
|
|
|
sk_memset32(pixels, notExpected, w*h);
|
2015-11-18 06:53:28 +00:00
|
|
|
REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes,
|
|
|
|
image->width() - w, image->height() - h));
|
|
|
|
REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
|
|
|
|
|
|
|
|
// partial top-left should succeed
|
2015-11-24 15:39:40 +00:00
|
|
|
sk_memset32(pixels, notExpected, w*h);
|
2015-11-18 06:53:28 +00:00
|
|
|
REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, -1, -1));
|
|
|
|
REPORTER_ASSERT(reporter, pixels[3] == expected);
|
|
|
|
REPORTER_ASSERT(reporter, has_pixels(pixels, w*h - 1, notExpected));
|
|
|
|
|
|
|
|
// partial bottom-right should succeed
|
2015-11-24 15:39:40 +00:00
|
|
|
sk_memset32(pixels, notExpected, w*h);
|
2015-11-18 06:53:28 +00:00
|
|
|
REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes,
|
|
|
|
image->width() - 1, image->height() - 1));
|
|
|
|
REPORTER_ASSERT(reporter, pixels[0] == expected);
|
|
|
|
REPORTER_ASSERT(reporter, has_pixels(&pixels[1], w*h - 1, notExpected));
|
|
|
|
}
|
2015-11-24 15:39:40 +00:00
|
|
|
DEF_TEST(ImageReadPixels, reporter) {
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image(create_image());
|
|
|
|
test_read_pixels(reporter, image.get());
|
2015-11-24 15:39:40 +00:00
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
image = create_data_image();
|
|
|
|
test_read_pixels(reporter, image.get());
|
2015-11-24 15:39:40 +00:00
|
|
|
|
|
|
|
RasterDataHolder dataHolder;
|
2016-03-17 17:51:11 +00:00
|
|
|
image = create_rasterproc_image(&dataHolder);
|
|
|
|
test_read_pixels(reporter, image.get());
|
2015-11-24 15:39:40 +00:00
|
|
|
image.reset();
|
|
|
|
REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount);
|
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
image = create_codec_image();
|
|
|
|
test_read_pixels(reporter, image.get());
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
|
|
|
#if SK_SUPPORT_GPU
|
2016-06-28 15:07:26 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageReadPixels_Gpu, reporter, ctxInfo) {
|
2016-05-11 13:33:06 +00:00
|
|
|
test_read_pixels(reporter, create_gpu_image(ctxInfo.grContext()).get());
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
|
|
|
#endif
|
2015-11-18 06:53:28 +00:00
|
|
|
|
|
|
|
static void check_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image,
|
|
|
|
const SkBitmap& bitmap, SkImage::LegacyBitmapMode mode) {
|
|
|
|
REPORTER_ASSERT(reporter, image->width() == bitmap.width());
|
|
|
|
REPORTER_ASSERT(reporter, image->height() == bitmap.height());
|
2016-08-17 21:01:05 +00:00
|
|
|
REPORTER_ASSERT(reporter, image->alphaType() == bitmap.alphaType());
|
2015-11-18 06:53:28 +00:00
|
|
|
|
|
|
|
if (SkImage::kRO_LegacyBitmapMode == mode) {
|
|
|
|
REPORTER_ASSERT(reporter, bitmap.isImmutable());
|
|
|
|
}
|
|
|
|
|
|
|
|
SkAutoLockPixels alp(bitmap);
|
|
|
|
REPORTER_ASSERT(reporter, bitmap.getPixels());
|
|
|
|
|
|
|
|
const SkImageInfo info = SkImageInfo::MakeN32(1, 1, bitmap.alphaType());
|
|
|
|
SkPMColor imageColor;
|
|
|
|
REPORTER_ASSERT(reporter, image->readPixels(info, &imageColor, sizeof(SkPMColor), 0, 0));
|
|
|
|
REPORTER_ASSERT(reporter, imageColor == *bitmap.getAddr32(0, 0));
|
|
|
|
}
|
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
static void test_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image, SkImage::LegacyBitmapMode mode) {
|
Make SkPngCodec decode progressively.
This is a step towards using SkCodec in Chromium, where progressive
decoding is necessary.
Switch from using png_read_row (which expects all the data to be
available) to png_process_data, which uses callbacks when rows are
available.
Create a new API for SkCodec, which supports progressive decoding and
scanline decoding. Future changes will switch the other clients off of
startScanlineDecode and get/skip-Scanlines to the new API.
Remove SkCodec::kNone_ScanlineOrder, which was only used for interlaced
PNG images. In the new API, interlaced PNG fits kTopDown. Also remove
updateCurrScanline(), which was only used by the old implementation for
interlaced PNG.
DMSrcSink:
- In CodecSrc::kScanline_Mode, use the new method for scanline decoding
for the supported formats (just PNG and PNG-in-ICO for now).
fuzz.cpp:
- Remove reference to kNone_ScanlineOrder
SkCodec:
- Add new APIs:
- startIncrementalDecode
- incrementalDecode
- Remove kNone_SkScanlineOrder and updateCurrScanline()
- Set fDstInfo and fOptions in getPixels(). This may not be necessary
for all implementations, but it simplifies things for SkPngCodec.
SkPngCodec:
- Implement new APIs
- Switch from sk_read_fn/png_read_row etc to png_process_data
- Expand AutoCleanPng's role to decode the header and create the
SkPngCodec
- Make the interlaced PNG decoder report how many lines were
initialized during an incomplete decode
SkIcoCodec:
- Implement the new APIs; supported for PNG in ICO
SkSampledCodec:
- Call the new method for decoding scanlines, and fall back to the old
method if the new version is unimplemented
- Remove references to kNone_SkScanlineOrder
tests/CodecPartial:
- Add a test which decodes part of an image, then finishes the decode,
and compares it to the straightforward method
tests/CodecTest:
- Add a test which decodes all scanlines using the new method
- Repurpose the Codec_stripes test to decode using the new method in
sections rather than all at once
- In the method check(), add a parameter for whether the image supports
the new method of scanline decoding, and be explicit about whether an
image supports incomplete
- Test incomplete PNG decodes. We should have been doing it anyway for
non-interlaced (except for an image that is too small - one row), but
the new method supports interlaced incomplete as well
- Make test_invalid_parameters test the new method
- Add a test to ensure that it's safe to fall back to scanline decoding without
rewinding
BUG=skia:4211
The new version was generally faster than the old version (but not significantly so).
Some raw performance differences can be found at https://docs.google.com/a/google.com/spreadsheets/d/1Gis3aRCEa72qBNDRMgGDg3jD-pMgO-FXldlNF9ejo4o/
Design doc can be found at https://docs.google.com/a/google.com/document/d/11Mn8-ePDKwVEMCjs3nWwSjxcSpJ_Cu8DF57KNtUmgLM/
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1997703003
Review-Url: https://codereview.chromium.org/1997703003
2016-09-16 15:20:38 +00:00
|
|
|
if (!image) {
|
|
|
|
ERRORF(reporter, "Failed to create image.");
|
|
|
|
return;
|
|
|
|
}
|
2015-11-24 15:39:40 +00:00
|
|
|
SkBitmap bitmap;
|
|
|
|
REPORTER_ASSERT(reporter, image->asLegacyBitmap(&bitmap, mode));
|
|
|
|
check_legacy_bitmap(reporter, image, bitmap, mode);
|
|
|
|
|
|
|
|
// Test subsetting to exercise the rowBytes logic.
|
|
|
|
SkBitmap tmp;
|
|
|
|
REPORTER_ASSERT(reporter, bitmap.extractSubset(&tmp, SkIRect::MakeWH(image->width() / 2,
|
|
|
|
image->height() / 2)));
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> subsetImage(SkImage::MakeFromBitmap(tmp));
|
|
|
|
REPORTER_ASSERT(reporter, subsetImage.get());
|
2015-11-24 15:39:40 +00:00
|
|
|
|
|
|
|
SkBitmap subsetBitmap;
|
|
|
|
REPORTER_ASSERT(reporter, subsetImage->asLegacyBitmap(&subsetBitmap, mode));
|
2016-03-17 17:51:11 +00:00
|
|
|
check_legacy_bitmap(reporter, subsetImage.get(), subsetBitmap, mode);
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
|
|
|
DEF_TEST(ImageLegacyBitmap, reporter) {
|
2015-11-18 06:53:28 +00:00
|
|
|
const SkImage::LegacyBitmapMode modes[] = {
|
|
|
|
SkImage::kRO_LegacyBitmapMode,
|
|
|
|
SkImage::kRW_LegacyBitmapMode,
|
|
|
|
};
|
2015-11-24 15:39:40 +00:00
|
|
|
for (auto& mode : modes) {
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image(create_image());
|
|
|
|
test_legacy_bitmap(reporter, image.get(), mode);
|
2015-11-18 06:53:28 +00:00
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
image = create_data_image();
|
|
|
|
test_legacy_bitmap(reporter, image.get(), mode);
|
2015-11-18 06:53:28 +00:00
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
RasterDataHolder dataHolder;
|
2016-03-17 17:51:11 +00:00
|
|
|
image = create_rasterproc_image(&dataHolder);
|
|
|
|
test_legacy_bitmap(reporter, image.get(), mode);
|
2015-11-24 15:39:40 +00:00
|
|
|
image.reset();
|
|
|
|
REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount);
|
2015-11-18 06:53:28 +00:00
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
image = create_codec_image();
|
|
|
|
test_legacy_bitmap(reporter, image.get(), mode);
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
|
|
|
}
|
2015-11-18 06:53:28 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageLegacyBitmap_Gpu, reporter, ctxInfo) {
|
2015-11-24 15:39:40 +00:00
|
|
|
const SkImage::LegacyBitmapMode modes[] = {
|
|
|
|
SkImage::kRO_LegacyBitmapMode,
|
|
|
|
SkImage::kRW_LegacyBitmapMode,
|
|
|
|
};
|
|
|
|
for (auto& mode : modes) {
|
2016-05-11 13:33:06 +00:00
|
|
|
sk_sp<SkImage> image(create_gpu_image(ctxInfo.grContext()));
|
2016-03-17 17:51:11 +00:00
|
|
|
test_legacy_bitmap(reporter, image.get(), mode);
|
2015-11-18 06:53:28 +00:00
|
|
|
}
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
2015-11-18 06:53:28 +00:00
|
|
|
#endif
|
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
static void test_peek(skiatest::Reporter* reporter, SkImage* image, bool expectPeekSuccess) {
|
Make SkPngCodec decode progressively.
This is a step towards using SkCodec in Chromium, where progressive
decoding is necessary.
Switch from using png_read_row (which expects all the data to be
available) to png_process_data, which uses callbacks when rows are
available.
Create a new API for SkCodec, which supports progressive decoding and
scanline decoding. Future changes will switch the other clients off of
startScanlineDecode and get/skip-Scanlines to the new API.
Remove SkCodec::kNone_ScanlineOrder, which was only used for interlaced
PNG images. In the new API, interlaced PNG fits kTopDown. Also remove
updateCurrScanline(), which was only used by the old implementation for
interlaced PNG.
DMSrcSink:
- In CodecSrc::kScanline_Mode, use the new method for scanline decoding
for the supported formats (just PNG and PNG-in-ICO for now).
fuzz.cpp:
- Remove reference to kNone_ScanlineOrder
SkCodec:
- Add new APIs:
- startIncrementalDecode
- incrementalDecode
- Remove kNone_SkScanlineOrder and updateCurrScanline()
- Set fDstInfo and fOptions in getPixels(). This may not be necessary
for all implementations, but it simplifies things for SkPngCodec.
SkPngCodec:
- Implement new APIs
- Switch from sk_read_fn/png_read_row etc to png_process_data
- Expand AutoCleanPng's role to decode the header and create the
SkPngCodec
- Make the interlaced PNG decoder report how many lines were
initialized during an incomplete decode
SkIcoCodec:
- Implement the new APIs; supported for PNG in ICO
SkSampledCodec:
- Call the new method for decoding scanlines, and fall back to the old
method if the new version is unimplemented
- Remove references to kNone_SkScanlineOrder
tests/CodecPartial:
- Add a test which decodes part of an image, then finishes the decode,
and compares it to the straightforward method
tests/CodecTest:
- Add a test which decodes all scanlines using the new method
- Repurpose the Codec_stripes test to decode using the new method in
sections rather than all at once
- In the method check(), add a parameter for whether the image supports
the new method of scanline decoding, and be explicit about whether an
image supports incomplete
- Test incomplete PNG decodes. We should have been doing it anyway for
non-interlaced (except for an image that is too small - one row), but
the new method supports interlaced incomplete as well
- Make test_invalid_parameters test the new method
- Add a test to ensure that it's safe to fall back to scanline decoding without
rewinding
BUG=skia:4211
The new version was generally faster than the old version (but not significantly so).
Some raw performance differences can be found at https://docs.google.com/a/google.com/spreadsheets/d/1Gis3aRCEa72qBNDRMgGDg3jD-pMgO-FXldlNF9ejo4o/
Design doc can be found at https://docs.google.com/a/google.com/document/d/11Mn8-ePDKwVEMCjs3nWwSjxcSpJ_Cu8DF57KNtUmgLM/
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1997703003
Review-Url: https://codereview.chromium.org/1997703003
2016-09-16 15:20:38 +00:00
|
|
|
if (!image) {
|
|
|
|
ERRORF(reporter, "Failed to create image!");
|
|
|
|
return;
|
|
|
|
}
|
2016-03-09 22:26:26 +00:00
|
|
|
SkPixmap pm;
|
|
|
|
bool success = image->peekPixels(&pm);
|
2015-11-24 15:39:40 +00:00
|
|
|
REPORTER_ASSERT(reporter, expectPeekSuccess == success);
|
|
|
|
if (success) {
|
2016-03-09 22:26:26 +00:00
|
|
|
const SkImageInfo& info = pm.info();
|
2015-11-24 15:39:40 +00:00
|
|
|
REPORTER_ASSERT(reporter, 20 == info.width());
|
|
|
|
REPORTER_ASSERT(reporter, 20 == info.height());
|
|
|
|
REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType());
|
|
|
|
REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() ||
|
|
|
|
kOpaque_SkAlphaType == info.alphaType());
|
2016-03-09 22:26:26 +00:00
|
|
|
REPORTER_ASSERT(reporter, info.minRowBytes() <= pm.rowBytes());
|
|
|
|
REPORTER_ASSERT(reporter, SkPreMultiplyColor(SK_ColorWHITE) == *pm.addr32(0, 0));
|
2015-11-18 06:53:28 +00:00
|
|
|
}
|
|
|
|
}
|
2015-11-24 15:39:40 +00:00
|
|
|
DEF_TEST(ImagePeek, reporter) {
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image(create_image());
|
|
|
|
test_peek(reporter, image.get(), true);
|
2015-11-18 06:53:28 +00:00
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
image = create_data_image();
|
|
|
|
test_peek(reporter, image.get(), true);
|
2015-11-18 06:53:28 +00:00
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
RasterDataHolder dataHolder;
|
2016-03-17 17:51:11 +00:00
|
|
|
image = create_rasterproc_image(&dataHolder);
|
|
|
|
test_peek(reporter, image.get(), true);
|
2015-11-24 15:39:40 +00:00
|
|
|
image.reset();
|
|
|
|
REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount);
|
2015-11-18 06:53:28 +00:00
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
image = create_codec_image();
|
|
|
|
test_peek(reporter, image.get(), false);
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
|
|
|
#if SK_SUPPORT_GPU
|
2016-06-28 15:07:26 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImagePeek_Gpu, reporter, ctxInfo) {
|
2016-05-11 13:33:06 +00:00
|
|
|
sk_sp<SkImage> image(create_gpu_image(ctxInfo.grContext()));
|
2016-03-17 17:51:11 +00:00
|
|
|
test_peek(reporter, image.get(), false);
|
2015-11-24 15:39:40 +00:00
|
|
|
}
|
|
|
|
#endif
|
2015-11-18 06:53:28 +00:00
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
|
|
|
struct TextureReleaseChecker {
|
|
|
|
TextureReleaseChecker() : fReleaseCount(0) {}
|
|
|
|
int fReleaseCount;
|
|
|
|
static void Release(void* self) {
|
|
|
|
static_cast<TextureReleaseChecker*>(self)->fReleaseCount++;
|
2015-11-18 06:53:28 +00:00
|
|
|
}
|
|
|
|
};
|
2016-07-22 14:22:04 +00:00
|
|
|
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkImage_NewFromTextureRelease, reporter, ctxInfo) {
|
|
|
|
const int kWidth = 10;
|
|
|
|
const int kHeight = 10;
|
2016-11-02 21:07:33 +00:00
|
|
|
std::unique_ptr<uint32_t[]> pixels(new uint32_t[kWidth * kHeight]);
|
2015-11-24 15:39:40 +00:00
|
|
|
GrBackendTextureDesc backendDesc;
|
2016-07-22 14:22:04 +00:00
|
|
|
backendDesc.fConfig = kRGBA_8888_GrPixelConfig;
|
2015-11-24 15:39:40 +00:00
|
|
|
backendDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
|
2016-07-22 14:22:04 +00:00
|
|
|
backendDesc.fWidth = kWidth;
|
|
|
|
backendDesc.fHeight = kHeight;
|
2015-11-24 15:39:40 +00:00
|
|
|
backendDesc.fSampleCnt = 0;
|
2016-07-22 14:22:04 +00:00
|
|
|
backendDesc.fTextureHandle = ctxInfo.grContext()->getGpu()->createTestingOnlyBackendTexture(
|
|
|
|
pixels.get(), kWidth, kHeight, kRGBA_8888_GrPixelConfig, true);
|
|
|
|
|
2015-11-24 15:39:40 +00:00
|
|
|
TextureReleaseChecker releaseChecker;
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> refImg(
|
2016-05-11 13:33:06 +00:00
|
|
|
SkImage::MakeFromTexture(ctxInfo.grContext(), backendDesc, kPremul_SkAlphaType,
|
2016-03-17 17:51:11 +00:00
|
|
|
TextureReleaseChecker::Release, &releaseChecker));
|
2015-11-24 15:39:40 +00:00
|
|
|
|
2015-11-18 06:53:28 +00:00
|
|
|
// Now exercise the release proc
|
2015-11-24 15:39:40 +00:00
|
|
|
REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
|
2015-11-18 06:53:28 +00:00
|
|
|
refImg.reset(nullptr); // force a release of the image
|
2015-11-24 15:39:40 +00:00
|
|
|
REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
|
2016-07-22 14:22:04 +00:00
|
|
|
|
|
|
|
ctxInfo.grContext()->getGpu()->deleteTestingOnlyBackendTexture(backendDesc.fTextureHandle);
|
2015-11-18 06:53:28 +00:00
|
|
|
}
|
2016-03-10 02:44:43 +00:00
|
|
|
|
|
|
|
static void check_images_same(skiatest::Reporter* reporter, const SkImage* a, const SkImage* b) {
|
|
|
|
if (a->width() != b->width() || a->height() != b->height()) {
|
|
|
|
ERRORF(reporter, "Images must have the same size");
|
|
|
|
return;
|
|
|
|
}
|
2016-08-17 21:01:05 +00:00
|
|
|
if (a->alphaType() != b->alphaType()) {
|
|
|
|
ERRORF(reporter, "Images must have the same alpha type");
|
2016-03-10 02:44:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkImageInfo info = SkImageInfo::MakeN32Premul(a->width(), a->height());
|
|
|
|
SkAutoPixmapStorage apm;
|
|
|
|
SkAutoPixmapStorage bpm;
|
|
|
|
|
|
|
|
apm.alloc(info);
|
|
|
|
bpm.alloc(info);
|
|
|
|
|
|
|
|
if (!a->readPixels(apm, 0, 0)) {
|
|
|
|
ERRORF(reporter, "Could not read image a's pixels");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!b->readPixels(bpm, 0, 0)) {
|
|
|
|
ERRORF(reporter, "Could not read image b's pixels");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto y = 0; y < info.height(); ++y) {
|
|
|
|
for (auto x = 0; x < info.width(); ++x) {
|
|
|
|
uint32_t pixelA = *apm.addr32(x, y);
|
|
|
|
uint32_t pixelB = *bpm.addr32(x, y);
|
|
|
|
if (pixelA != pixelB) {
|
|
|
|
ERRORF(reporter, "Expected image pixels to be the same. At %d,%d 0x%08x != 0x%08x",
|
|
|
|
x, y, pixelA, pixelB);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-28 15:07:26 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(NewTextureFromPixmap, reporter, ctxInfo) {
|
2016-03-10 15:22:25 +00:00
|
|
|
for (auto create : {&create_image,
|
|
|
|
&create_image_565,
|
|
|
|
&create_image_ct}) {
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image((*create)());
|
2016-03-10 02:44:43 +00:00
|
|
|
if (!image) {
|
|
|
|
ERRORF(reporter, "Could not create image");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPixmap pixmap;
|
|
|
|
if (!image->peekPixels(&pixmap)) {
|
|
|
|
ERRORF(reporter, "peek failed");
|
|
|
|
} else {
|
2016-05-11 13:33:06 +00:00
|
|
|
sk_sp<SkImage> texImage(SkImage::MakeTextureFromPixmap(ctxInfo.grContext(), pixmap,
|
2016-03-17 17:51:11 +00:00
|
|
|
SkBudgeted::kNo));
|
2016-03-10 02:44:43 +00:00
|
|
|
if (!texImage) {
|
|
|
|
ERRORF(reporter, "NewTextureFromPixmap failed.");
|
|
|
|
} else {
|
2016-03-17 17:51:11 +00:00
|
|
|
check_images_same(reporter, image.get(), texImage.get());
|
2016-03-10 02:44:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-14 14:53:13 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredTextureImage, reporter, ctxInfo) {
|
2016-05-11 13:33:06 +00:00
|
|
|
GrContext* context = ctxInfo.grContext();
|
2016-05-11 17:38:05 +00:00
|
|
|
sk_gpu_test::TestContext* testContext = ctxInfo.testContext();
|
2016-10-27 16:30:08 +00:00
|
|
|
sk_sp<GrContextThreadSafeProxy> proxy = context->threadSafeProxy();
|
2016-03-11 14:46:33 +00:00
|
|
|
|
|
|
|
GrContextFactory otherFactory;
|
2016-04-05 19:59:06 +00:00
|
|
|
ContextInfo otherContextInfo =
|
2016-04-05 18:06:27 +00:00
|
|
|
otherFactory.getContextInfo(GrContextFactory::kNativeGL_ContextType);
|
2016-03-11 14:46:33 +00:00
|
|
|
|
2016-05-11 17:38:05 +00:00
|
|
|
testContext->makeCurrent();
|
2016-03-11 14:46:33 +00:00
|
|
|
REPORTER_ASSERT(reporter, proxy);
|
2017-01-03 16:35:56 +00:00
|
|
|
auto createLarge = [context] {
|
|
|
|
return create_image_large(context->caps()->maxTextureSize());
|
|
|
|
};
|
2016-03-11 14:46:33 +00:00
|
|
|
struct {
|
2016-06-13 18:18:14 +00:00
|
|
|
std::function<sk_sp<SkImage> ()> fImageFactory;
|
|
|
|
std::vector<SkImage::DeferredTextureImageUsageParams> fParams;
|
|
|
|
SkFilterQuality fExpectedQuality;
|
|
|
|
int fExpectedScaleFactor;
|
|
|
|
bool fExpectation;
|
2016-03-11 14:46:33 +00:00
|
|
|
} testCases[] = {
|
2016-08-30 19:09:23 +00:00
|
|
|
{ create_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
|
|
|
kNone_SkFilterQuality, 1, true },
|
|
|
|
{ create_codec_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
|
|
|
kNone_SkFilterQuality, 1, true },
|
|
|
|
{ create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
|
|
|
kNone_SkFilterQuality, 1, true },
|
|
|
|
{ create_picture_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
|
|
|
kNone_SkFilterQuality, 1, false },
|
|
|
|
{ [context] { return create_gpu_image(context); },
|
|
|
|
{{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
|
|
|
kNone_SkFilterQuality, 1, false },
|
2016-03-11 14:46:33 +00:00
|
|
|
// Create a texture image in a another GrContext.
|
2016-05-11 17:38:05 +00:00
|
|
|
{ [testContext, otherContextInfo] {
|
|
|
|
otherContextInfo.testContext()->makeCurrent();
|
2016-05-11 13:33:06 +00:00
|
|
|
sk_sp<SkImage> otherContextImage = create_gpu_image(otherContextInfo.grContext());
|
2016-05-11 17:38:05 +00:00
|
|
|
testContext->makeCurrent();
|
2016-03-11 14:46:33 +00:00
|
|
|
return otherContextImage;
|
2016-08-30 19:09:23 +00:00
|
|
|
}, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
|
|
|
kNone_SkFilterQuality, 1, false },
|
2016-06-13 18:18:14 +00:00
|
|
|
// Create an image that is too large to upload.
|
2017-01-03 16:35:56 +00:00
|
|
|
{ createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
2016-08-30 19:09:23 +00:00
|
|
|
kNone_SkFilterQuality, 1, false },
|
2016-06-13 18:18:14 +00:00
|
|
|
// Create an image that is too large, but is scaled to an acceptable size.
|
2017-01-03 16:35:56 +00:00
|
|
|
{ createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
|
2016-06-13 18:18:14 +00:00
|
|
|
kMedium_SkFilterQuality, 16, true},
|
|
|
|
// Create an image with multiple low filter qualities, make sure we round up.
|
2017-01-03 16:35:56 +00:00
|
|
|
{ createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 4},
|
|
|
|
{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
|
2016-06-13 18:18:14 +00:00
|
|
|
kMedium_SkFilterQuality, 16, true},
|
|
|
|
// Create an image with multiple prescale levels, make sure we chose the minimum scale.
|
2017-01-03 16:35:56 +00:00
|
|
|
{ createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 5},
|
|
|
|
{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
|
2016-06-13 18:18:14 +00:00
|
|
|
kMedium_SkFilterQuality, 16, true},
|
2016-03-11 14:46:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (auto testCase : testCases) {
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image(testCase.fImageFactory());
|
Make SkPngCodec decode progressively.
This is a step towards using SkCodec in Chromium, where progressive
decoding is necessary.
Switch from using png_read_row (which expects all the data to be
available) to png_process_data, which uses callbacks when rows are
available.
Create a new API for SkCodec, which supports progressive decoding and
scanline decoding. Future changes will switch the other clients off of
startScanlineDecode and get/skip-Scanlines to the new API.
Remove SkCodec::kNone_ScanlineOrder, which was only used for interlaced
PNG images. In the new API, interlaced PNG fits kTopDown. Also remove
updateCurrScanline(), which was only used by the old implementation for
interlaced PNG.
DMSrcSink:
- In CodecSrc::kScanline_Mode, use the new method for scanline decoding
for the supported formats (just PNG and PNG-in-ICO for now).
fuzz.cpp:
- Remove reference to kNone_ScanlineOrder
SkCodec:
- Add new APIs:
- startIncrementalDecode
- incrementalDecode
- Remove kNone_SkScanlineOrder and updateCurrScanline()
- Set fDstInfo and fOptions in getPixels(). This may not be necessary
for all implementations, but it simplifies things for SkPngCodec.
SkPngCodec:
- Implement new APIs
- Switch from sk_read_fn/png_read_row etc to png_process_data
- Expand AutoCleanPng's role to decode the header and create the
SkPngCodec
- Make the interlaced PNG decoder report how many lines were
initialized during an incomplete decode
SkIcoCodec:
- Implement the new APIs; supported for PNG in ICO
SkSampledCodec:
- Call the new method for decoding scanlines, and fall back to the old
method if the new version is unimplemented
- Remove references to kNone_SkScanlineOrder
tests/CodecPartial:
- Add a test which decodes part of an image, then finishes the decode,
and compares it to the straightforward method
tests/CodecTest:
- Add a test which decodes all scanlines using the new method
- Repurpose the Codec_stripes test to decode using the new method in
sections rather than all at once
- In the method check(), add a parameter for whether the image supports
the new method of scanline decoding, and be explicit about whether an
image supports incomplete
- Test incomplete PNG decodes. We should have been doing it anyway for
non-interlaced (except for an image that is too small - one row), but
the new method supports interlaced incomplete as well
- Make test_invalid_parameters test the new method
- Add a test to ensure that it's safe to fall back to scanline decoding without
rewinding
BUG=skia:4211
The new version was generally faster than the old version (but not significantly so).
Some raw performance differences can be found at https://docs.google.com/a/google.com/spreadsheets/d/1Gis3aRCEa72qBNDRMgGDg3jD-pMgO-FXldlNF9ejo4o/
Design doc can be found at https://docs.google.com/a/google.com/document/d/11Mn8-ePDKwVEMCjs3nWwSjxcSpJ_Cu8DF57KNtUmgLM/
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1997703003
Review-Url: https://codereview.chromium.org/1997703003
2016-09-16 15:20:38 +00:00
|
|
|
if (!image) {
|
|
|
|
ERRORF(reporter, "Failed to create image!");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-06-13 18:18:14 +00:00
|
|
|
size_t size = image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
|
|
|
|
static_cast<int>(testCase.fParams.size()),
|
2016-10-17 13:51:37 +00:00
|
|
|
nullptr, nullptr);
|
2016-03-11 14:46:33 +00:00
|
|
|
static const char *const kFS[] = { "fail", "succeed" };
|
|
|
|
if (SkToBool(size) != testCase.fExpectation) {
|
|
|
|
ERRORF(reporter, "This image was expected to %s but did not.",
|
|
|
|
kFS[testCase.fExpectation]);
|
|
|
|
}
|
|
|
|
if (size) {
|
|
|
|
void* buffer = sk_malloc_throw(size);
|
|
|
|
void* misaligned = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(buffer) + 3);
|
2016-06-13 18:18:14 +00:00
|
|
|
if (image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
|
|
|
|
static_cast<int>(testCase.fParams.size()),
|
2016-10-17 13:51:37 +00:00
|
|
|
misaligned, nullptr)) {
|
2016-03-11 14:46:33 +00:00
|
|
|
ERRORF(reporter, "Should fail when buffer is misaligned.");
|
|
|
|
}
|
2016-06-13 18:18:14 +00:00
|
|
|
if (!image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
|
|
|
|
static_cast<int>(testCase.fParams.size()),
|
2016-10-17 13:51:37 +00:00
|
|
|
buffer, nullptr)) {
|
2016-03-11 14:46:33 +00:00
|
|
|
ERRORF(reporter, "deferred image size succeeded but creation failed.");
|
|
|
|
} else {
|
|
|
|
for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) {
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> newImage(
|
|
|
|
SkImage::MakeFromDeferredTextureImageData(context, buffer, budgeted));
|
2016-03-16 17:28:35 +00:00
|
|
|
REPORTER_ASSERT(reporter, newImage != nullptr);
|
2016-03-11 14:46:33 +00:00
|
|
|
if (newImage) {
|
2016-06-13 18:18:14 +00:00
|
|
|
// Scale the image in software for comparison.
|
|
|
|
SkImageInfo scaled_info = SkImageInfo::MakeN32(
|
2016-08-17 21:01:05 +00:00
|
|
|
image->width() / testCase.fExpectedScaleFactor,
|
|
|
|
image->height() / testCase.fExpectedScaleFactor,
|
|
|
|
image->alphaType());
|
2016-06-13 18:18:14 +00:00
|
|
|
SkAutoPixmapStorage scaled;
|
|
|
|
scaled.alloc(scaled_info);
|
|
|
|
image->scalePixels(scaled, testCase.fExpectedQuality);
|
|
|
|
sk_sp<SkImage> scaledImage = SkImage::MakeRasterCopy(scaled);
|
|
|
|
check_images_same(reporter, scaledImage.get(), newImage.get());
|
2016-03-11 14:46:33 +00:00
|
|
|
}
|
|
|
|
// The other context should not be able to create images from texture data
|
|
|
|
// created by the original context.
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> newImage2(SkImage::MakeFromDeferredTextureImageData(
|
2016-05-11 13:33:06 +00:00
|
|
|
otherContextInfo.grContext(), buffer, budgeted));
|
2016-03-11 14:46:33 +00:00
|
|
|
REPORTER_ASSERT(reporter, !newImage2);
|
2016-05-11 17:38:05 +00:00
|
|
|
testContext->makeCurrent();
|
2016-03-11 14:46:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
sk_free(buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-11-18 06:53:28 +00:00
|
|
|
#endif
|
2016-07-27 02:42:04 +00:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static void make_all_premul(SkBitmap* bm) {
|
|
|
|
bm->allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType));
|
|
|
|
for (int a = 0; a < 256; ++a) {
|
|
|
|
for (int r = 0; r < 256; ++r) {
|
|
|
|
// make all valid premul combinations
|
|
|
|
int c = SkTMin(a, r);
|
|
|
|
*bm->getAddr32(a, r) = SkPackARGB32(a, c, c, c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool equal(const SkBitmap& a, const SkBitmap& b) {
|
|
|
|
SkASSERT(a.width() == b.width());
|
|
|
|
SkASSERT(a.height() == b.height());
|
|
|
|
for (int y = 0; y < a.height(); ++y) {
|
2016-11-28 22:26:27 +00:00
|
|
|
for (int x = 0; x < a.width(); ++x) {
|
|
|
|
SkPMColor pa = *a.getAddr32(x, y);
|
|
|
|
SkPMColor pb = *b.getAddr32(x, y);
|
|
|
|
if (pa != pb) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-27 02:42:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(image_roundtrip_encode, reporter) {
|
|
|
|
SkBitmap bm0;
|
|
|
|
make_all_premul(&bm0);
|
|
|
|
|
|
|
|
auto img0 = SkImage::MakeFromBitmap(bm0);
|
2016-11-23 15:55:18 +00:00
|
|
|
sk_sp<SkData> data(img0->encode(SkEncodedImageFormat::kPNG, 100));
|
2016-07-27 02:42:04 +00:00
|
|
|
auto img1 = SkImage::MakeFromEncoded(data);
|
2016-08-02 23:02:05 +00:00
|
|
|
|
2016-07-27 02:42:04 +00:00
|
|
|
SkBitmap bm1;
|
|
|
|
bm1.allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType));
|
|
|
|
img1->readPixels(bm1.info(), bm1.getPixels(), bm1.rowBytes(), 0, 0);
|
2016-08-02 23:02:05 +00:00
|
|
|
|
2016-07-27 02:42:04 +00:00
|
|
|
REPORTER_ASSERT(reporter, equal(bm0, bm1));
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(image_roundtrip_premul, reporter) {
|
|
|
|
SkBitmap bm0;
|
|
|
|
make_all_premul(&bm0);
|
|
|
|
|
|
|
|
SkBitmap bm1;
|
|
|
|
bm1.allocPixels(SkImageInfo::MakeN32(256, 256, kUnpremul_SkAlphaType));
|
|
|
|
bm0.readPixels(bm1.info(), bm1.getPixels(), bm1.rowBytes(), 0, 0);
|
|
|
|
|
|
|
|
SkBitmap bm2;
|
|
|
|
bm2.allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType));
|
|
|
|
bm1.readPixels(bm2.info(), bm2.getPixels(), bm2.rowBytes(), 0, 0);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, equal(bm0, bm2));
|
|
|
|
}
|