Example #1
0
void render(Map& map, OffscreenView& view) {
    PremultipliedImage result;
    map.renderStill(view, [&](std::exception_ptr) {
        result = view.readStillImage();
    });

    while (!result.valid()) {
        util::RunLoop::Get()->runOnce();
    }
}
Example #2
0
PremultipliedImage render(Map& map) {
    PremultipliedImage result;
    map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
        result = std::move(image);
    });

    while (!result.size()) {
        util::RunLoop::Get()->runOnce();
    }

    return result;
}
PremultipliedImage premultiply(UnassociatedImage&& src) {
    PremultipliedImage dst;

    dst.size = src.size;
    dst.data = std::move(src.data);

    uint8_t* data = dst.data.get();
    for (size_t i = 0; i < dst.bytes(); i += 4) {
        uint8_t& r = data[i + 0];
        uint8_t& g = data[i + 1];
        uint8_t& b = data[i + 2];
        uint8_t& a = data[i + 3];
        r = (r * a + 127) / 255;
        g = (g * a + 127) / 255;
        b = (b * a + 127) / 255;
    }

    return dst;
}
PremultipliedImage HeadlessView::readStillImage() {
    assert(active);

    const unsigned int w = dimensions[0] * pixelRatio;
    const unsigned int h = dimensions[1] * pixelRatio;

    PremultipliedImage image { w, h };
    MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));

    const auto stride = image.stride();
    auto tmp = std::make_unique<uint8_t[]>(stride);
    uint8_t* rgba = image.data.get();
    for (int i = 0, j = h - 1; i < j; i++, j--) {
        std::memcpy(tmp.get(), rgba + i * stride, stride);
        std::memcpy(rgba + i * stride, rgba + j * stride, stride);
        std::memcpy(rgba + j * stride, tmp.get(), stride);
    }

    return image;
}
PremultipliedImage HeadlessView::readStillImage(std::array<uint16_t, 2> size) {
    assert(active);

    if (!size[0] || !size[1]) {
        size[0] = dimensions[0] * pixelRatio;
        size[1] = dimensions[1] * pixelRatio;
    }

    PremultipliedImage image { size[0], size[1] };
    MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));

    const auto stride = image.stride();
    auto tmp = std::make_unique<uint8_t[]>(stride);
    uint8_t* rgba = image.data.get();
    for (int i = 0, j = size[1] - 1; i < j; i++, j--) {
        std::memcpy(tmp.get(), rgba + i * stride, stride);
        std::memcpy(rgba + i * stride, rgba + j * stride, stride);
        std::memcpy(rgba + j * stride, tmp.get(), stride);
    }

    return image;
}
// Encode PNGs without libpng.
std::string encodePNG(const PremultipliedImage& pre) {
    // Make copy of the image so that we can unpremultiply it.
    const auto src = util::unpremultiply(pre.clone());

    // PNG magic bytes
    const char preamble[8] = { char(0x89), 'P', 'N', 'G', '\r', '\n', 0x1a, '\n' };

    // IHDR chunk for our RGBA image.
    const char ihdr[13] = {
        NETWORK_BYTE_UINT32(src.size.width),  // width
        NETWORK_BYTE_UINT32(src.size.height), // height
        8,                                    // bit depth == 8 bits
        6,                                    // color type == RGBA
        0,                                    // compression method == deflate
        0,                                    // filter method == default
        0,                                    // interlace method == none
    };

    // Prepare the (compressed) data chunk.
    const auto stride = src.stride();
    std::string idat;
    for (uint32_t y = 0; y < src.size.height; y++) {
        // Every scanline needs to be prefixed with one byte that indicates the filter type.
        idat.append(1, 0); // filter type 0
        idat.append((const char*)(src.data.get() + y * stride), stride);
    }
    idat = util::compress(idat);

    // Assemble the PNG.
    std::string png;
    png.reserve((8 /* preamble */) + (12 + 13 /* IHDR */) +
                (12 + idat.size() /* IDAT */) + (12 /* IEND */));
    png.append(preamble, 8);
    addChunk(png, "IHDR", ihdr, 13);
    addChunk(png, "IDAT", idat.data(), static_cast<uint32_t>(idat.size()));
    addChunk(png, "IEND");
    return png;
}
TEST(API, RepeatedRender) {
    using namespace mbgl;

    util::RunLoop loop;

    const auto style = util::read_file("test/fixtures/api/water.json");

    auto display = std::make_shared<mbgl::HeadlessDisplay>();
    HeadlessView view(display, 1, 256, 512);
#ifdef MBGL_ASSET_ZIP
    // Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/`
    DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip");
#else
    DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
#endif

    Log::setObserver(std::make_unique<FixtureLogObserver>());

    Map map(view, fileSource, MapMode::Still);

    {
        map.setStyleJSON(style, "");
        PremultipliedImage result;
        map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
            result = std::move(image);
        });

        while (!result.size()) {
            loop.runOnce();
        }

        ASSERT_EQ(256, result.width);
        ASSERT_EQ(512, result.height);
#if !TEST_READ_ONLY
        util::write_file("test/fixtures/api/1.png", encodePNG(result));
#endif
    }

    {
        map.setStyleJSON(style, "");
        PremultipliedImage result;
        map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
            result = std::move(image);
        });

        while (!result.size()) {
            loop.runOnce();
        }

        ASSERT_EQ(256, result.width);
        ASSERT_EQ(512, result.height);
#if !TEST_READ_ONLY
        util::write_file("test/fixtures/api/2.png", encodePNG(result));
#endif
    }

    auto observer = Log::removeObserver();
    auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
    auto unchecked = flo->unchecked();
    EXPECT_TRUE(unchecked.empty()) << unchecked;
}
Example #8
0
uint64_t crc64(const PremultipliedImage &image) {
    return crc64(reinterpret_cast<const char*>(image.data.get()), image.size());
}
std::shared_ptr<SpriteImage> namedMarker(const std::string &name) {
    PremultipliedImage image = decodeImage(util::read_file("test/fixtures/sprites/" + name));
    return std::make_shared<SpriteImage>(image.width, image.height, 1.0, std::string(reinterpret_cast<char*>(image.data.get()), image.size()));
}