<commit_before>#include "node_map.hpp"
#include "node_file_source.hpp"

#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/map/still_image.hpp>
#include <mbgl/util/exception.hpp>

#include <unistd.h>

namespace node_mbgl {

struct NodeMap::RenderOptions {
    double zoom = 0;
    double bearing = 0;
    double latitude = 0;
    double longitude = 0;
    unsigned int width = 512;
    unsigned int height = 512;
    float ratio = 1.0f;
    std::vector<std::string> classes;
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Static Node Methods

v8::Persistent<v8::FunctionTemplate> NodeMap::constructorTemplate;

static std::shared_ptr<mbgl::HeadlessDisplay> sharedDisplay() {
    static auto display = std::make_shared<mbgl::HeadlessDisplay>();
    return display;
}

void NodeMap::Init(v8::Handle<v8::Object> target) {
    NanScope();

    v8::Local<v8::FunctionTemplate> t = NanNew<v8::FunctionTemplate>(New);

    t->InstanceTemplate()->SetInternalFieldCount(1);
    t->SetClassName(NanNew("Map"));

    NODE_SET_PROTOTYPE_METHOD(t, "setAccessToken", SetAccessToken);
    NODE_SET_PROTOTYPE_METHOD(t, "load", Load);
    NODE_SET_PROTOTYPE_METHOD(t, "render", Render);

    NanAssignPersistent(constructorTemplate, t);

    target->Set(NanNew("Map"), t->GetFunction());

    // Initialize display connection on module load.
    sharedDisplay();
}

NAN_METHOD(NodeMap::New) {
    NanScope();

    if (!args.IsConstructCall()) {
        return NanThrowTypeError("Use the new operator to create new Map objects");
    }

    if (args.Length() < 1 || !NanHasInstance(NodeFileSource::constructorTemplate, args[0])) {
        return NanThrowTypeError("Requires a FileSource as first argument");
    }

    auto source = args[0]->ToObject();

    // Check that request() and cancel() are defined.
    if (!source->Has(NanNew("request")) || !source->Get(NanNew("request"))->IsFunction()) {
        return NanThrowError("FileSource must have a request member function");
    }
    if (!source->Has(NanNew("cancel")) || !source->Get(NanNew("cancel"))->IsFunction()) {
        return NanThrowError("FileSource must have a cancel member function");
    }

    try {
        auto map = new NodeMap(source);
        map->Wrap(args.This());
    } catch(std::exception &ex) {
        return NanThrowError(ex.what());
    }

    NanReturnValue(args.This());
}

NAN_METHOD(NodeMap::SetAccessToken) {
    NanScope();

    if (args.Length() < 1) {
        return NanThrowError("Requires a string as first argument");
    }

    NanUtf8String token(args[0]);

    auto nodeMap = node::ObjectWrap::Unwrap<NodeMap>(args.Holder());

    if (nodeMap->map.isRendering()) {
        return NanThrowError("Map object is currently in use");
    }

    try {
        nodeMap->map.setAccessToken(std::string { *token, size_t(token.length()) });
    } catch (const std::exception &ex) {
        return NanThrowError(ex.what());
    }

    NanReturnUndefined();
}

const std::string StringifyStyle(v8::Handle<v8::Value> styleHandle) {
    NanScope();

    auto JSON = NanGetCurrentContext()->Global()->Get(NanNew("JSON"))->ToObject();
    auto stringify = v8::Handle<v8::Function>::Cast(JSON->Get(NanNew("stringify")));

    return *NanUtf8String(stringify->Call(JSON, 1, &styleHandle));
}

NAN_METHOD(NodeMap::Load) {
    NanScope();

    if (args.Length() < 1) {
        return NanThrowError("Requires a map style as first argument");
    }

    std::string style;

    if (args[0]->IsObject()) {
        style = StringifyStyle(args[0]);
    } else if (args[0]->IsString()) {
        NanUtf8String string(args[0]);
        style = { *string, size_t(string.length()) };
    } else {
        return NanThrowTypeError("First argument must be a string or object");
    }

    auto nodeMap = node::ObjectWrap::Unwrap<NodeMap>(args.Holder());

    if (nodeMap->map.isRendering()) {
        return NanThrowError("Map object is currently in use");
    }

    try {
        nodeMap->map.setStyleJSON(style, ".");
    } catch (const std::exception &ex) {
        return NanThrowError(ex.what());
    }

    NanReturnUndefined();
}

std::unique_ptr<NodeMap::RenderOptions> NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
    NanScope();

    auto options = mbgl::util::make_unique<RenderOptions>();

    if (obj->Has(NanNew("zoom"))) { options->zoom = obj->Get(NanNew("zoom"))->NumberValue(); }
    if (obj->Has(NanNew("bearing"))) { options->bearing = obj->Get(NanNew("bearing"))->NumberValue(); }
    if (obj->Has(NanNew("center"))) {
        auto center = obj->Get(NanNew("center")).As<v8::Array>();
        if (center->Length() > 0) { options->latitude = center->Get(0)->NumberValue(); }
        if (center->Length() > 1) { options->longitude = center->Get(1)->NumberValue(); }
    }
    if (obj->Has(NanNew("width"))) { options->width = obj->Get(NanNew("width"))->IntegerValue(); }
    if (obj->Has(NanNew("height"))) { options->height = obj->Get(NanNew("height"))->IntegerValue(); }
    if (obj->Has(NanNew("ratio"))) { options->ratio = obj->Get(NanNew("ratio"))->IntegerValue(); }

    if (obj->Has(NanNew("classes"))) {
        auto classes = obj->Get(NanNew("classes"))->ToObject().As<v8::Array>();
        const int length = classes->Length();
        options->classes.reserve(length);
        for (int i = 0; i < length; i++) {
            options->classes.push_back(std::string { *NanUtf8String(classes->Get(i)->ToString()) });
        }
    }

    return options;
}

NAN_METHOD(NodeMap::Render) {
    NanScope();

    if (args.Length() <= 0 || !args[0]->IsObject()) {
        return NanThrowTypeError("First argument must be an options object");
    }

    if (args.Length() <= 1 || !args[1]->IsFunction()) {
        return NanThrowTypeError("Second argument must be a callback function");
    }

    auto options = ParseOptions(args[0]->ToObject());

    auto nodeMap = node::ObjectWrap::Unwrap<NodeMap>(args.Holder());

    if (nodeMap->map.isRendering()) {
        return NanThrowError("Map object is currently in use");
    }

    assert(!nodeMap->callback);
    assert(!nodeMap->image);
    nodeMap->callback = std::unique_ptr<NanCallback>(new NanCallback(args[1].As<v8::Function>()));

    try {
        nodeMap->startRender(std::move(options));
    } catch (mbgl::util::Exception &ex) {
        return NanThrowError(ex.what());
    }

    NanReturnUndefined();
}

void NodeMap::startRender(std::unique_ptr<NodeMap::RenderOptions> options) {
    view.resize(options->width, options->height, options->ratio);
    map.setClasses(options->classes);
    map.setLatLngZoom(mbgl::LatLng(options->latitude, options->longitude), options->zoom);
    map.setBearing(options->bearing);

    map.renderStill([this](std::unique_ptr<const mbgl::StillImage> result) {
        assert(!image);
        image = std::move(result);
        uv_async_send(async);
    });

    // Retain this object, otherwise it might get destructed before we are finished rendering the
    // still image.
    Ref();

    // Similarly, we're now waiting for the async to be called, so we need to make sure that it
    // keeps the loop alive.
    uv_ref(reinterpret_cast<uv_handle_t *>(async));
}

void NodeMap::renderFinished() {
    NanScope();

    // We're done with this render call, so we're unrefing so that the loop could close.
    uv_unref(reinterpret_cast<uv_handle_t *>(async));

    // There is no render pending anymore, we the GC could now delete this object if it went out
    // of scope.
    Unref();

    // Move the callback and image out of the way so that the callback can start a new render call.
    auto cb = std::move(callback);
    auto img = std::move(image);
    assert(cb);

    // These have to be empty to be prepared for the next render call.
    assert(!callback);
    assert(!image);

    if (img) {
        v8::Local<v8::Object> result = v8::Object::New();
        result->Set(NanNew("width"), NanNew(img->width));
        result->Set(NanNew("height"), NanNew(img->height));

        v8::Local<v8::Object> pixels = NanNewBufferHandle(
            reinterpret_cast<char *>(img->pixels.get()),
            size_t(img->width) * size_t(img->height) * sizeof(mbgl::StillImage::Pixel),

            // Retain the StillImage object until the buffer is deleted.
            [](char *, void *hint) {
                delete reinterpret_cast<const mbgl::StillImage *>(hint);
            },
            const_cast<mbgl::StillImage *>(img.get())
        );
        img.release();

        result->Set(NanNew("pixels"), pixels);

        v8::Local<v8::Value> argv[] = {
            NanNull(),
            result,
        };
        cb->Call(2, argv);
    } else {
        v8::Local<v8::Value> argv[] = {
            NanError("Didn't get an image")
        };
        cb->Call(1, argv);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////
// Instance

NodeMap::NodeMap(v8::Handle<v8::Object> source_) :
    source(source_),
    view(sharedDisplay()),
    fs(*ObjectWrap::Unwrap<NodeFileSource>(source_)),
    map(view, fs, mbgl::Map::RenderMode::Still),
    async(new uv_async_t) {

    async->data = this;
    uv_async_init(uv_default_loop(), async, [](uv_async_t *as, int) {
        reinterpret_cast<NodeMap *>(as->data)->renderFinished();
    });

    // Make sure the async handle doesn't keep the loop alive.
    uv_unref(reinterpret_cast<uv_handle_t *>(async));
}

NodeMap::~NodeMap() {
    source.Dispose();

    uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *handle) {
        delete reinterpret_cast<uv_async_t *>(handle);
    });
}

}
<commit_msg>NanAssignPersistent source in NodeMap constructor, fixes #109<commit_after>#include "node_map.hpp"
#include "node_file_source.hpp"

#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/map/still_image.hpp>
#include <mbgl/util/exception.hpp>

#include <unistd.h>

namespace node_mbgl {

struct NodeMap::RenderOptions {
    double zoom = 0;
    double bearing = 0;
    double latitude = 0;
    double longitude = 0;
    unsigned int width = 512;
    unsigned int height = 512;
    float ratio = 1.0f;
    std::vector<std::string> classes;
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Static Node Methods

v8::Persistent<v8::FunctionTemplate> NodeMap::constructorTemplate;

static std::shared_ptr<mbgl::HeadlessDisplay> sharedDisplay() {
    static auto display = std::make_shared<mbgl::HeadlessDisplay>();
    return display;
}

void NodeMap::Init(v8::Handle<v8::Object> target) {
    NanScope();

    v8::Local<v8::FunctionTemplate> t = NanNew<v8::FunctionTemplate>(New);

    t->InstanceTemplate()->SetInternalFieldCount(1);
    t->SetClassName(NanNew("Map"));

    NODE_SET_PROTOTYPE_METHOD(t, "setAccessToken", SetAccessToken);
    NODE_SET_PROTOTYPE_METHOD(t, "load", Load);
    NODE_SET_PROTOTYPE_METHOD(t, "render", Render);

    NanAssignPersistent(constructorTemplate, t);

    target->Set(NanNew("Map"), t->GetFunction());

    // Initialize display connection on module load.
    sharedDisplay();
}

NAN_METHOD(NodeMap::New) {
    NanScope();

    if (!args.IsConstructCall()) {
        return NanThrowTypeError("Use the new operator to create new Map objects");
    }

    if (args.Length() < 1 || !NanHasInstance(NodeFileSource::constructorTemplate, args[0])) {
        return NanThrowTypeError("Requires a FileSource as first argument");
    }

    auto source = args[0]->ToObject();

    // Check that request() and cancel() are defined.
    if (!source->Has(NanNew("request")) || !source->Get(NanNew("request"))->IsFunction()) {
        return NanThrowError("FileSource must have a request member function");
    }
    if (!source->Has(NanNew("cancel")) || !source->Get(NanNew("cancel"))->IsFunction()) {
        return NanThrowError("FileSource must have a cancel member function");
    }

    try {
        auto map = new NodeMap(source);
        map->Wrap(args.This());
    } catch(std::exception &ex) {
        return NanThrowError(ex.what());
    }

    NanReturnValue(args.This());
}

NAN_METHOD(NodeMap::SetAccessToken) {
    NanScope();

    if (args.Length() < 1) {
        return NanThrowError("Requires a string as first argument");
    }

    NanUtf8String token(args[0]);

    auto nodeMap = node::ObjectWrap::Unwrap<NodeMap>(args.Holder());

    if (nodeMap->map.isRendering()) {
        return NanThrowError("Map object is currently in use");
    }

    try {
        nodeMap->map.setAccessToken(std::string { *token, size_t(token.length()) });
    } catch (const std::exception &ex) {
        return NanThrowError(ex.what());
    }

    NanReturnUndefined();
}

const std::string StringifyStyle(v8::Handle<v8::Value> styleHandle) {
    NanScope();

    auto JSON = NanGetCurrentContext()->Global()->Get(NanNew("JSON"))->ToObject();
    auto stringify = v8::Handle<v8::Function>::Cast(JSON->Get(NanNew("stringify")));

    return *NanUtf8String(stringify->Call(JSON, 1, &styleHandle));
}

NAN_METHOD(NodeMap::Load) {
    NanScope();

    if (args.Length() < 1) {
        return NanThrowError("Requires a map style as first argument");
    }

    std::string style;

    if (args[0]->IsObject()) {
        style = StringifyStyle(args[0]);
    } else if (args[0]->IsString()) {
        NanUtf8String string(args[0]);
        style = { *string, size_t(string.length()) };
    } else {
        return NanThrowTypeError("First argument must be a string or object");
    }

    auto nodeMap = node::ObjectWrap::Unwrap<NodeMap>(args.Holder());

    if (nodeMap->map.isRendering()) {
        return NanThrowError("Map object is currently in use");
    }

    try {
        nodeMap->map.setStyleJSON(style, ".");
    } catch (const std::exception &ex) {
        return NanThrowError(ex.what());
    }

    NanReturnUndefined();
}

std::unique_ptr<NodeMap::RenderOptions> NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
    NanScope();

    auto options = mbgl::util::make_unique<RenderOptions>();

    if (obj->Has(NanNew("zoom"))) { options->zoom = obj->Get(NanNew("zoom"))->NumberValue(); }
    if (obj->Has(NanNew("bearing"))) { options->bearing = obj->Get(NanNew("bearing"))->NumberValue(); }
    if (obj->Has(NanNew("center"))) {
        auto center = obj->Get(NanNew("center")).As<v8::Array>();
        if (center->Length() > 0) { options->latitude = center->Get(0)->NumberValue(); }
        if (center->Length() > 1) { options->longitude = center->Get(1)->NumberValue(); }
    }
    if (obj->Has(NanNew("width"))) { options->width = obj->Get(NanNew("width"))->IntegerValue(); }
    if (obj->Has(NanNew("height"))) { options->height = obj->Get(NanNew("height"))->IntegerValue(); }
    if (obj->Has(NanNew("ratio"))) { options->ratio = obj->Get(NanNew("ratio"))->IntegerValue(); }

    if (obj->Has(NanNew("classes"))) {
        auto classes = obj->Get(NanNew("classes"))->ToObject().As<v8::Array>();
        const int length = classes->Length();
        options->classes.reserve(length);
        for (int i = 0; i < length; i++) {
            options->classes.push_back(std::string { *NanUtf8String(classes->Get(i)->ToString()) });
        }
    }

    return options;
}

NAN_METHOD(NodeMap::Render) {
    NanScope();

    if (args.Length() <= 0 || !args[0]->IsObject()) {
        return NanThrowTypeError("First argument must be an options object");
    }

    if (args.Length() <= 1 || !args[1]->IsFunction()) {
        return NanThrowTypeError("Second argument must be a callback function");
    }

    auto options = ParseOptions(args[0]->ToObject());

    auto nodeMap = node::ObjectWrap::Unwrap<NodeMap>(args.Holder());

    if (nodeMap->map.isRendering()) {
        return NanThrowError("Map object is currently in use");
    }

    assert(!nodeMap->callback);
    assert(!nodeMap->image);
    nodeMap->callback = std::unique_ptr<NanCallback>(new NanCallback(args[1].As<v8::Function>()));

    try {
        nodeMap->startRender(std::move(options));
    } catch (mbgl::util::Exception &ex) {
        return NanThrowError(ex.what());
    }

    NanReturnUndefined();
}

void NodeMap::startRender(std::unique_ptr<NodeMap::RenderOptions> options) {
    view.resize(options->width, options->height, options->ratio);
    map.setClasses(options->classes);
    map.setLatLngZoom(mbgl::LatLng(options->latitude, options->longitude), options->zoom);
    map.setBearing(options->bearing);

    map.renderStill([this](std::unique_ptr<const mbgl::StillImage> result) {
        assert(!image);
        image = std::move(result);
        uv_async_send(async);
    });

    // Retain this object, otherwise it might get destructed before we are finished rendering the
    // still image.
    Ref();

    // Similarly, we're now waiting for the async to be called, so we need to make sure that it
    // keeps the loop alive.
    uv_ref(reinterpret_cast<uv_handle_t *>(async));
}

void NodeMap::renderFinished() {
    NanScope();

    // We're done with this render call, so we're unrefing so that the loop could close.
    uv_unref(reinterpret_cast<uv_handle_t *>(async));

    // There is no render pending anymore, we the GC could now delete this object if it went out
    // of scope.
    Unref();

    // Move the callback and image out of the way so that the callback can start a new render call.
    auto cb = std::move(callback);
    auto img = std::move(image);
    assert(cb);

    // These have to be empty to be prepared for the next render call.
    assert(!callback);
    assert(!image);

    if (img) {
        v8::Local<v8::Object> result = v8::Object::New();
        result->Set(NanNew("width"), NanNew(img->width));
        result->Set(NanNew("height"), NanNew(img->height));

        v8::Local<v8::Object> pixels = NanNewBufferHandle(
            reinterpret_cast<char *>(img->pixels.get()),
            size_t(img->width) * size_t(img->height) * sizeof(mbgl::StillImage::Pixel),

            // Retain the StillImage object until the buffer is deleted.
            [](char *, void *hint) {
                delete reinterpret_cast<const mbgl::StillImage *>(hint);
            },
            const_cast<mbgl::StillImage *>(img.get())
        );
        img.release();

        result->Set(NanNew("pixels"), pixels);

        v8::Local<v8::Value> argv[] = {
            NanNull(),
            result,
        };
        cb->Call(2, argv);
    } else {
        v8::Local<v8::Value> argv[] = {
            NanError("Didn't get an image")
        };
        cb->Call(1, argv);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////
// Instance

NodeMap::NodeMap(v8::Handle<v8::Object> source_) :
    view(sharedDisplay()),
    fs(*ObjectWrap::Unwrap<NodeFileSource>(source_)),
    map(view, fs, mbgl::Map::RenderMode::Still),
    async(new uv_async_t) {

    NanAssignPersistent(source, source_);

    async->data = this;
    uv_async_init(uv_default_loop(), async, [](uv_async_t *as, int) {
        reinterpret_cast<NodeMap *>(as->data)->renderFinished();
    });

    // Make sure the async handle doesn't keep the loop alive.
    uv_unref(reinterpret_cast<uv_handle_t *>(async));
}

NodeMap::~NodeMap() {
    source.Dispose();

    uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *handle) {
        delete reinterpret_cast<uv_async_t *>(handle);
    });
}

}
<|endoftext|>


<commit_before>
#include <gtest/gtest.h>

#include <zinc/zincconfigure.h>
#include <zinc/context.h>
#include <zinc/core.h>
#include <zinc/field.h>
#include <zinc/fieldcache.h>
#include <zinc/fieldcomposite.h>
#include <zinc/fieldconstant.h>
#include <zinc/fieldimage.h>
#include <zinc/fieldimageprocessing.h>
#include <zinc/fieldmodule.h>
#include <zinc/region.h>
#include <zinc/status.h>
#include <zinc/stream.h>

#include "zinctestsetup.hpp"
#include "test_resources.h"

TEST(cmzn_fieldmodule_create_field_connected_threshold_image_filter, invalid_args)
{
	const double values[] = { 0.3, 0.1, 0.7 };
	ZincTestSetup zinc;

	cmzn_field_id f1 = cmzn_fieldmodule_create_field_curvature_anisotropic_diffusion_image_filter(zinc.fm, 0, 0.2, 1.0, 1);
	EXPECT_EQ((cmzn_field_id)0, f1);

	cmzn_field_id f2 = cmzn_fieldmodule_create_field_connected_threshold_image_filter(zinc.fm, 0, 0.2, 1.0, 1.0, 1, 3, values);
	EXPECT_EQ((cmzn_field_id)0, f2);

}

TEST(cmzn_fieldmodule_create_field_curvature_anisotropic_diffusion_image_filter, valid_args)
{
	ZincTestSetup zinc;

	// Create empty image field
	cmzn_field_id f1 = cmzn_fieldmodule_create_field_image(zinc.fm);
	EXPECT_NE(static_cast<cmzn_field_id>(0), f1);

	cmzn_field_image_id im = cmzn_field_cast_image(f1);
	cmzn_streaminformation_id si = cmzn_field_image_create_streaminformation(im);
	EXPECT_NE(static_cast<cmzn_streaminformation_id>(0), si);

	cmzn_streamresource_id sr = cmzn_streaminformation_create_streamresource_file(si, TestResources::getLocation(TestResources::TESTIMAGE_GRAY_JPG_RESOURCE));

	EXPECT_EQ(CMZN_OK, cmzn_field_image_read(im, si));

	cmzn_field_id f2 = cmzn_fieldmodule_create_field_curvature_anisotropic_diffusion_image_filter(zinc.fm, cmzn_field_image_base_cast(im), 0.1, 1.0, 1);
	EXPECT_NE((cmzn_field_id)0, f2);

//	cmzn_field_id f2 = cmzn_fieldmodule_create_field_connected_threshold_image_filter(zinc.fm, f1, 0.2, 1.0, 1.0, 1, 3, values);
//	EXPECT_NE((cmzn_field_id)0, f2);

	cmzn_field_id xi = cmzn_field_image_get_domain_field(im);
	EXPECT_NE((cmzn_field_id)0, xi);

	cmzn_fieldcache_id cache = cmzn_fieldmodule_create_fieldcache(zinc.fm);

	double location[] = { 0.7, 0.2};
	double value = 0.0;

	EXPECT_EQ(CMZN_OK, cmzn_fieldcache_set_field_real(cache, xi, 2, location));
//	EXPECT_EQ(CMZN_OK, cmzn_field_evaluate_real(cmzn_field_image_base_cast(im), cache, 1, &value));

	EXPECT_EQ(CMZN_OK, cmzn_field_evaluate_real(f2, cache, 1, &value));
	EXPECT_NEAR(0.211765, value, 1e-6);

	cmzn_fieldcache_destroy(&cache);
	cmzn_field_destroy(&xi);
	cmzn_field_destroy(&f1);
	cmzn_field_destroy(&f2);
	cmzn_streamresource_destroy(&sr);
	cmzn_streaminformation_destroy(&si);
	cmzn_field_image_destroy(&im);

}

TEST(cmzn_fieldmodule_create_field_connected_threshold_image_filter, valid_args)
{
	ZincTestSetup zinc;
	double location[] = { 0.7, 0.2};

	// Create empty image field
	cmzn_field_id f1 = cmzn_fieldmodule_create_field_image(zinc.fm);
	EXPECT_NE(static_cast<cmzn_field_id>(0), f1);

	cmzn_field_image_id im = cmzn_field_cast_image(f1);
	cmzn_streaminformation_id si = cmzn_field_image_create_streaminformation(im);
	EXPECT_NE(static_cast<cmzn_streaminformation_id>(0), si);

	cmzn_streamresource_id sr = cmzn_streaminformation_create_streamresource_file(si, TestResources::getLocation(TestResources::TESTIMAGE_GRAY_JPG_RESOURCE));

	EXPECT_EQ(CMZN_OK, cmzn_field_image_read(im, si));

	cmzn_field_id f2 = cmzn_fieldmodule_create_field_connected_threshold_image_filter(zinc.fm, cmzn_field_image_base_cast(im), 0.2, 0.22, 0.33, 1, 2, location);
	EXPECT_NE((cmzn_field_id)0, f2);

	cmzn_field_id xi = cmzn_field_image_get_domain_field(im);
	EXPECT_NE((cmzn_field_id)0, xi);

	cmzn_fieldcache_id cache = cmzn_fieldmodule_create_fieldcache(zinc.fm);

	double value = 0.0;
	EXPECT_EQ(CMZN_OK, cmzn_fieldcache_set_field_real(cache, xi, 2, location));
//	EXPECT_EQ(CMZN_OK, cmzn_field_evaluate_real(cmzn_field_image_base_cast(im), cache, 1, &value));

	EXPECT_EQ(CMZN_OK, cmzn_field_evaluate_real(f2, cache, 1, &value));
	EXPECT_NEAR(0.33, value, 1e-6);

	cmzn_fieldcache_destroy(&cache);
	cmzn_field_destroy(&xi);
	cmzn_field_destroy(&f1);
	cmzn_field_destroy(&f2);
	cmzn_streamresource_destroy(&sr);
	cmzn_streaminformation_destroy(&si);
	cmzn_field_image_destroy(&im);
}
<commit_msg>Updated unit tests for image processing APIs. https://tracker.physiomeproject.org/show_bug.cgi?id=3662<commit_after>
#include <gtest/gtest.h>

#include <zinc/zincconfigure.h>
#include <zinc/context.h>
#include <zinc/core.h>
#include <zinc/field.h>
#include <zinc/fieldcache.h>
#include <zinc/fieldcomposite.h>
#include <zinc/fieldconstant.h>
#include <zinc/fieldimage.h>
#include <zinc/fieldimageprocessing.h>
#include <zinc/fieldmodule.h>
#include <zinc/region.h>
#include <zinc/status.h>
#include <zinc/stream.h>

#include "zinctestsetup.hpp"
#include "test_resources.h"

TEST(cmzn_fieldmodule_create_field_imagefilter_connected_threshold, invalid_args)
{
	const double values[] = { 0.3, 0.1, 0.7 };
	ZincTestSetup zinc;

	cmzn_field_id f1 = cmzn_fieldmodule_create_field_imagefilter_curvature_anisotropic_diffusion(zinc.fm, 0, 0.2, 1.0, 1);
	EXPECT_EQ((cmzn_field_id)0, f1);

	cmzn_field_id f2 = cmzn_fieldmodule_create_field_imagefilter_connected_threshold(zinc.fm, 0, 0.2, 1.0, 1.0, 1, 3, values);
	EXPECT_EQ((cmzn_field_id)0, f2);

}

TEST(cmzn_fieldmodule_create_field_imagefilter_curvature_anisotropic_diffusion, valid_args)
{
	ZincTestSetup zinc;

	// Create empty image field
	cmzn_field_id f1 = cmzn_fieldmodule_create_field_image(zinc.fm);
	EXPECT_NE(static_cast<cmzn_field_id>(0), f1);

	cmzn_field_image_id im = cmzn_field_cast_image(f1);
	cmzn_streaminformation_id si = cmzn_field_image_create_streaminformation(im);
	EXPECT_NE(static_cast<cmzn_streaminformation_id>(0), si);

	cmzn_streamresource_id sr = cmzn_streaminformation_create_streamresource_file(si, TestResources::getLocation(TestResources::TESTIMAGE_GRAY_JPG_RESOURCE));

	EXPECT_EQ(CMZN_OK, cmzn_field_image_read(im, si));

	cmzn_field_id f2 = cmzn_fieldmodule_create_field_imagefilter_curvature_anisotropic_diffusion(zinc.fm, cmzn_field_image_base_cast(im), 0.1, 1.0, 1);
	EXPECT_NE((cmzn_field_id)0, f2);

//	cmzn_field_id f2 = cmzn_fieldmodule_create_field_connected_threshold_image_filter(zinc.fm, f1, 0.2, 1.0, 1.0, 1, 3, values);
//	EXPECT_NE((cmzn_field_id)0, f2);

	cmzn_field_id xi = cmzn_field_image_get_domain_field(im);
	EXPECT_NE((cmzn_field_id)0, xi);

	cmzn_fieldcache_id cache = cmzn_fieldmodule_create_fieldcache(zinc.fm);

	double location[] = { 0.7, 0.2};
	double value = 0.0;

	EXPECT_EQ(CMZN_OK, cmzn_fieldcache_set_field_real(cache, xi, 2, location));
//	EXPECT_EQ(CMZN_OK, cmzn_field_evaluate_real(cmzn_field_image_base_cast(im), cache, 1, &value));

	EXPECT_EQ(CMZN_OK, cmzn_field_evaluate_real(f2, cache, 1, &value));
	EXPECT_NEAR(0.211765, value, 1e-6);

	cmzn_fieldcache_destroy(&cache);
	cmzn_field_destroy(&xi);
	cmzn_field_destroy(&f1);
	cmzn_field_destroy(&f2);
	cmzn_streamresource_destroy(&sr);
	cmzn_streaminformation_destroy(&si);
	cmzn_field_image_destroy(&im);

}

TEST(cmzn_fieldmodule_create_field_imagefilter_connected_threshold, valid_args)
{
	ZincTestSetup zinc;
	double location[] = { 0.7, 0.2};

	// Create empty image field
	cmzn_field_id f1 = cmzn_fieldmodule_create_field_image(zinc.fm);
	EXPECT_NE(static_cast<cmzn_field_id>(0), f1);

	cmzn_field_image_id im = cmzn_field_cast_image(f1);
	cmzn_streaminformation_id si = cmzn_field_image_create_streaminformation(im);
	EXPECT_NE(static_cast<cmzn_streaminformation_id>(0), si);

	cmzn_streamresource_id sr = cmzn_streaminformation_create_streamresource_file(si, TestResources::getLocation(TestResources::TESTIMAGE_GRAY_JPG_RESOURCE));

	EXPECT_EQ(CMZN_OK, cmzn_field_image_read(im, si));

	cmzn_field_id f2 = cmzn_fieldmodule_create_field_imagefilter_connected_threshold(zinc.fm, cmzn_field_image_base_cast(im), 0.2, 0.22, 0.33, 1, 2, location);
	EXPECT_NE((cmzn_field_id)0, f2);

	cmzn_field_id xi = cmzn_field_image_get_domain_field(im);
	EXPECT_NE((cmzn_field_id)0, xi);

	cmzn_fieldcache_id cache = cmzn_fieldmodule_create_fieldcache(zinc.fm);

	double value = 0.0;
	EXPECT_EQ(CMZN_OK, cmzn_fieldcache_set_field_real(cache, xi, 2, location));
//	EXPECT_EQ(CMZN_OK, cmzn_field_evaluate_real(cmzn_field_image_base_cast(im), cache, 1, &value));

	EXPECT_EQ(CMZN_OK, cmzn_field_evaluate_real(f2, cache, 1, &value));
	EXPECT_NEAR(0.33, value, 1e-6);

	cmzn_fieldcache_destroy(&cache);
	cmzn_field_destroy(&xi);
	cmzn_field_destroy(&f1);
	cmzn_field_destroy(&f2);
	cmzn_streamresource_destroy(&sr);
	cmzn_streaminformation_destroy(&si);
	cmzn_field_image_destroy(&im);
}
<|endoftext|>


<commit_before>/*=====================================================================

QGroundControl Open Source Ground Control Station

(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Implementation of class Core
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QFile>
#include <QFlags>
#include <QThread>
#include <QSplashScreen>
#include <QPixmap>
#include <QDesktopWidget>
#include <QPainter>
#include <QStyleFactory>
#include <QAction>

#include <QDebug>

#include "configuration.h"
#include "QGC.h"
#include "Core.h"
#include "MG.h"
#include "MainWindow.h"
#include "GAudioOutput.h"

#ifdef OPAL_RT
#include "OpalLink.h"
#endif
#include "UDPLink.h"
#include "MAVLinkSimulationLink.h"

/**
 * @brief Constructor for the main application.
 *
 * This constructor initializes and starts the whole application. It takes standard
 * command-line parameters
 *
 * @param argc The number of command-line parameters
 * @param argv The string array of parameters
 **/

Core::Core(int &argc, char* argv[]) : QApplication(argc, argv)
{
    // Set application name
    this->setApplicationName(QGC_APPLICATION_NAME);
    this->setApplicationVersion(QGC_APPLICATION_VERSION);
    this->setOrganizationName(QLatin1String("OPENMAV"));
    this->setOrganizationDomain("org.qgroundcontrol");

    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);

    // Check application settings
    // clear them if they mismatch
    // QGC then falls back to default
    QSettings settings;
    if (settings.contains("QGC_APPLICATION_VERSION"))
    {
        QString qgcVersion = settings.value("QGC_APPLICATION_VERSION").toString();
        if (qgcVersion != QGC_APPLICATION_VERSION)
        {
            settings.clear();
            // Write current application version
            settings.setValue("QGC_APPLICATION_VERSION", QGC_APPLICATION_VERSION);
        }
    }
    else
    {
        // If application version is not set, clear settings anyway
        settings.clear();
        // Write current application version
        settings.setValue("QGC_APPLICATION_VERSION", QGC_APPLICATION_VERSION);
    }

    settings.sync();

    // Show splash screen
    QPixmap splashImage(":images/splash.png");
    QSplashScreen* splashScreen = new QSplashScreen(splashImage, Qt::WindowStaysOnTopHint);
    // Delete splash screen after mainWindow was displayed
    splashScreen->setAttribute(Qt::WA_DeleteOnClose);
    splashScreen->show();
    splashScreen->showMessage(tr("Loading application fonts"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));

    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

    // Load application font
    QFontDatabase fontDatabase = QFontDatabase();
    const QString fontFileName = ":/general/vera.ttf"; ///< Font file is part of the QRC file and compiled into the app
    const QString fontFamilyName = "Bitstream Vera Sans";
    if(!QFile::exists(fontFileName)) printf("ERROR! font file: %s DOES NOT EXIST!\n", fontFileName.toStdString().c_str());
    fontDatabase.addApplicationFont(fontFileName);
    // Avoid Using setFont(). In the Qt docu you can read the following:
    //     "Warning: Do not use this function in conjunction with Qt Style Sheets."
    // setFont(fontDatabase.font(fontFamilyName, "Roman", 12));

    // Start the comm link manager
    splashScreen->showMessage(tr("Starting Communication Links"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    startLinkManager();

    // Start the UAS Manager
    splashScreen->showMessage(tr("Starting UAS Manager"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    startUASManager();

    //tarsus = new ViconTarsusProtocol();
    //tarsus->start();

    // Start the user interface
    splashScreen->showMessage(tr("Starting User Interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    // Start UI

    // Connect links
    // to make sure that all components are initialized when the
    // first messages arrive
    UDPLink* udpLink = new UDPLink(QHostAddress::Any, 14550);
    // Listen on Multicast-Address 239.255.77.77, Port 14550
    //QHostAddress * multicast_udp = new QHostAddress("239.255.77.77");
    //UDPLink* udpLink = new UDPLink(*multicast_udp, 14550);
    //mainWindow->addLink(udpLink);

#ifdef OPAL_RT
    // Add OpalRT Link, but do not connect
    OpalLink* opalLink = new OpalLink();
    //mainWindow->addLink(opalLink);
#endif
    // MAVLinkSimulationLink* simulationLink = new MAVLinkSimulationLink(MG::DIR::getSupportFilesDirectory() + "/demo-log.txt");
    MAVLinkSimulationLink* simulationLink = new MAVLinkSimulationLink(":/demo-log.txt");
    simulationLink->disconnect();
    //mainWindow->addLink(simulationLink);

    mainWindow = MainWindow::instance();
        // Remove splash screen
    splashScreen->finish(mainWindow);

    // Check if link could be connected
    if (!udpLink->connect())
    {
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Critical);
        msgBox.setText("Could not connect UDP port. Is an instance of " + qAppName() + "already running?");
        msgBox.setInformativeText("You will not be able to receive data via UDP. Please check that you're running the right executable and then re-start " + qAppName() + ". Do you want to close the application?");
        msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
        msgBox.setDefaultButton(QMessageBox::No);
        int ret = msgBox.exec();

        // Close the message box shortly after the click to prevent accidental clicks
        QTimer::singleShot(5000, &msgBox, SLOT(reject()));

        // Exit application
        if (ret == QMessageBox::Yes)
        {
            //mainWindow->close();
            QTimer::singleShot(200, mainWindow, SLOT(close()));
        }
    }
}

/**
 * @brief Destructor for the groundstation. It destroys all loaded instances.
 *
 **/
Core::~Core()
{
    //mainWindow->storeSettings();
    mainWindow->hide();
    mainWindow->deleteLater();
    // Delete singletons
    delete LinkManager::instance();
    delete UASManager::instance();
}

/**
 * @brief Start the link managing component.
 *
 * The link manager keeps track of all communication links and provides the global
 * packet queue. It is the main communication hub
 **/
void Core::startLinkManager()
{
    LinkManager::instance();
}

/**
 * @brief Start the Unmanned Air System Manager
 *
 **/
void Core::startUASManager()
{
    // Load UAS plugins
    QDir pluginsDir = QDir(qApp->applicationDirPath());

#if defined(Q_OS_WIN)
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();
#elif defined(Q_OS_LINUX)
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
    if (pluginsDir.dirName() == "MacOS")
    {
        pluginsDir.cdUp();
        pluginsDir.cdUp();
        pluginsDir.cdUp();
    }
#endif
    pluginsDir.cd("plugins");

    UASManager::instance();

    // Load plugins

    QStringList pluginFileNames;

    foreach (QString fileName, pluginsDir.entryList(QDir::Files))
    {
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = loader.instance();
        if (plugin)
        {
            //populateMenus(plugin);
            pluginFileNames += fileName;
            //printf(QString("Loaded plugin from " + fileName + "\n").toStdString().c_str());
        }
    }
}

<commit_msg>Added application version check<commit_after>/*=====================================================================

QGroundControl Open Source Ground Control Station

(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Implementation of class Core
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QFile>
#include <QFlags>
#include <QThread>
#include <QSplashScreen>
#include <QPixmap>
#include <QDesktopWidget>
#include <QPainter>
#include <QStyleFactory>
#include <QAction>

#include <QDebug>

#include "configuration.h"
#include "QGC.h"
#include "Core.h"
#include "MG.h"
#include "MainWindow.h"
#include "GAudioOutput.h"

#ifdef OPAL_RT
#include "OpalLink.h"
#endif
#include "UDPLink.h"
#include "MAVLinkSimulationLink.h"

/**
 * @brief Constructor for the main application.
 *
 * This constructor initializes and starts the whole application. It takes standard
 * command-line parameters
 *
 * @param argc The number of command-line parameters
 * @param argv The string array of parameters
 **/

Core::Core(int &argc, char* argv[]) : QApplication(argc, argv)
{
    // Set application name
    this->setApplicationName(QGC_APPLICATION_NAME);
    this->setApplicationVersion(QGC_APPLICATION_VERSION);
    this->setOrganizationName(QLatin1String("OPENMAV"));
    this->setOrganizationDomain("org.qgroundcontrol");

    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);

    // Check application settings
    // clear them if they mismatch
    // QGC then falls back to default
    QSettings settings;

    // Show user an upgrade message if QGC got upgraded
    bool upgraded = false;
    QString lastApplicationVersion("");
    if (settings.contains("QGC_APPLICATION_VERSION"))
    {
        QString qgcVersion = settings.value("QGC_APPLICATION_VERSION").toString();
        if (qgcVersion != QGC_APPLICATION_VERSION)
        {
            lastApplicationVersion = qgcVersion;
            settings.clear();
            // Write current application version
            settings.setValue("QGC_APPLICATION_VERSION", QGC_APPLICATION_VERSION);
            upgraded = true;
        }
    }
    else
    {
        // If application version is not set, clear settings anyway
        settings.clear();
        // Write current application version
        settings.setValue("QGC_APPLICATION_VERSION", QGC_APPLICATION_VERSION);
    }

    settings.sync();

    // Show splash screen
    QPixmap splashImage(":images/splash.png");
    QSplashScreen* splashScreen = new QSplashScreen(splashImage, Qt::WindowStaysOnTopHint);
    // Delete splash screen after mainWindow was displayed
    splashScreen->setAttribute(Qt::WA_DeleteOnClose);
    splashScreen->show();
    splashScreen->showMessage(tr("Loading application fonts"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));

    // Exit main application when last window is closed
    connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));

    // Load application font
    QFontDatabase fontDatabase = QFontDatabase();
    const QString fontFileName = ":/general/vera.ttf"; ///< Font file is part of the QRC file and compiled into the app
    const QString fontFamilyName = "Bitstream Vera Sans";
    if(!QFile::exists(fontFileName)) printf("ERROR! font file: %s DOES NOT EXIST!\n", fontFileName.toStdString().c_str());
    fontDatabase.addApplicationFont(fontFileName);
    // Avoid Using setFont(). In the Qt docu you can read the following:
    //     "Warning: Do not use this function in conjunction with Qt Style Sheets."
    // setFont(fontDatabase.font(fontFamilyName, "Roman", 12));

    // Start the comm link manager
    splashScreen->showMessage(tr("Starting Communication Links"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    startLinkManager();

    // Start the UAS Manager
    splashScreen->showMessage(tr("Starting UAS Manager"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    startUASManager();

    //tarsus = new ViconTarsusProtocol();
    //tarsus->start();

    // Start the user interface
    splashScreen->showMessage(tr("Starting User Interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    // Start UI

    // Connect links
    // to make sure that all components are initialized when the
    // first messages arrive
    UDPLink* udpLink = new UDPLink(QHostAddress::Any, 14550);
    // Listen on Multicast-Address 239.255.77.77, Port 14550
    //QHostAddress * multicast_udp = new QHostAddress("239.255.77.77");
    //UDPLink* udpLink = new UDPLink(*multicast_udp, 14550);
    //mainWindow->addLink(udpLink);

#ifdef OPAL_RT
    // Add OpalRT Link, but do not connect
    OpalLink* opalLink = new OpalLink();
    //mainWindow->addLink(opalLink);
#endif
    // MAVLinkSimulationLink* simulationLink = new MAVLinkSimulationLink(MG::DIR::getSupportFilesDirectory() + "/demo-log.txt");
    MAVLinkSimulationLink* simulationLink = new MAVLinkSimulationLink(":/demo-log.txt");
    simulationLink->disconnect();
    //mainWindow->addLink(simulationLink);

    mainWindow = MainWindow::instance();

    // Remove splash screen
    splashScreen->finish(mainWindow);

    if (upgraded) mainWindow->showInfoMessage(tr("Default Settings Loaded"), tr("QGroundControl has been upgraded from version %1 to version %2. Some of your user preferences have been reset to defaults for safety reasons. Please adjust them where needed.").arg(lastApplicationVersion).arg(QGC_APPLICATION_VERSION));

    // Check if link could be connected
    if (!udpLink->connect())
    {
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Critical);
        msgBox.setText("Could not connect UDP port. Is an instance of " + qAppName() + "already running?");
        msgBox.setInformativeText("You will not be able to receive data via UDP. Please check that you're running the right executable and then re-start " + qAppName() + ". Do you want to close the application?");
        msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
        msgBox.setDefaultButton(QMessageBox::No);
        int ret = msgBox.exec();

        // Close the message box shortly after the click to prevent accidental clicks
        QTimer::singleShot(5000, &msgBox, SLOT(reject()));

        // Exit application
        if (ret == QMessageBox::Yes)
        {
            //mainWindow->close();
            QTimer::singleShot(200, mainWindow, SLOT(close()));
        }
    }
}

/**
 * @brief Destructor for the groundstation. It destroys all loaded instances.
 *
 **/
Core::~Core()
{
    //mainWindow->storeSettings();
    mainWindow->hide();
    mainWindow->deleteLater();
    // Delete singletons
    delete LinkManager::instance();
    delete UASManager::instance();
}

/**
 * @brief Start the link managing component.
 *
 * The link manager keeps track of all communication links and provides the global
 * packet queue. It is the main communication hub
 **/
void Core::startLinkManager()
{
    LinkManager::instance();
}

/**
 * @brief Start the Unmanned Air System Manager
 *
 **/
void Core::startUASManager()
{
    // Load UAS plugins
    QDir pluginsDir = QDir(qApp->applicationDirPath());

#if defined(Q_OS_WIN)
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();
#elif defined(Q_OS_LINUX)
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
    if (pluginsDir.dirName() == "MacOS")
    {
        pluginsDir.cdUp();
        pluginsDir.cdUp();
        pluginsDir.cdUp();
    }
#endif
    pluginsDir.cd("plugins");

    UASManager::instance();

    // Load plugins

    QStringList pluginFileNames;

    foreach (QString fileName, pluginsDir.entryList(QDir::Files))
    {
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = loader.instance();
        if (plugin)
        {
            //populateMenus(plugin);
            pluginFileNames += fileName;
            //printf(QString("Loaded plugin from " + fileName + "\n").toStdString().c_str());
        }
    }
}

<|endoftext|>


<commit_before>//===--- Context.cpp -----------------------------------------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//

#include "Context.h"
#include "llvm/Config/config.h"
#include <cassert>

// The thread-local Context is scoped in a function to avoid init-order issues.
// It's created by currentContext() when first needed.

#ifdef HAVE_PTHREAD_GETSPECIFIC
// We'd love to use thread_local everywhere.
// It requires support from the runtime: __cxa_thread_atexit.
// Rather than detect this, we use the pthread API where available.
#include <pthread.h>
static clang::clangd::Context &currentContext() {
  using clang::clangd::Context;
  static pthread_key_t CtxKey;

  // Once (across threads), set up pthread TLS for Context, and its destructor.
  static int Dummy = [] { // Create key only once, for all threads.
    if (auto Err = pthread_key_create(&CtxKey, /*destructor=*/+[](void *Ctx) {
          delete reinterpret_cast<Context *>(Ctx);
        }))
      llvm_unreachable(strerror(Err));
    return 0;
  }();
  (void)Dummy;

  // Now grab the current context from TLS, and create it if it doesn't exist.
  void *Ctx = pthread_getspecific(CtxKey);
  if (!Ctx) {
    Ctx = new Context();
    if (auto Err = pthread_setspecific(CtxKey, Ctx))
      llvm_unreachable(strerror(Err));
  }
  return *reinterpret_cast<Context *>(Ctx);
}
#else
// Only supported platform without pthread is windows, and thread_local works.
static clang::clangd::Context &currentContext() {
  static thread_local auto C = clang::clangd::Context::empty();
  return C;
}
#endif

namespace clang {
namespace clangd {

Context Context::empty() { return Context(/*Data=*/nullptr); }

Context::Context(std::shared_ptr<const Data> DataPtr)
    : DataPtr(std::move(DataPtr)) {}

Context Context::clone() const { return Context(DataPtr); }

const Context &Context::current() { return currentContext(); }

Context Context::swapCurrent(Context Replacement) {
  std::swap(Replacement, currentContext());
  return Replacement;
}

} // namespace clangd
} // namespace clang
<commit_msg>[clangd] Add a cstring include for strerror.<commit_after>//===--- Context.cpp -----------------------------------------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//

#include "Context.h"
#include "llvm/Config/config.h"
#include <cassert>

// The thread-local Context is scoped in a function to avoid init-order issues.
// It's created by currentContext() when first needed.

#ifdef HAVE_PTHREAD_GETSPECIFIC
// We'd love to use thread_local everywhere.
// It requires support from the runtime: __cxa_thread_atexit.
// Rather than detect this, we use the pthread API where available.
#include <pthread.h>
#include <cstring>
static clang::clangd::Context &currentContext() {
  using clang::clangd::Context;
  static pthread_key_t CtxKey;

  // Once (across threads), set up pthread TLS for Context, and its destructor.
  static int Dummy = [] { // Create key only once, for all threads.
    if (auto Err = pthread_key_create(&CtxKey, /*destructor=*/+[](void *Ctx) {
          delete reinterpret_cast<Context *>(Ctx);
        }))
      llvm_unreachable(strerror(Err));
    return 0;
  }();
  (void)Dummy;

  // Now grab the current context from TLS, and create it if it doesn't exist.
  void *Ctx = pthread_getspecific(CtxKey);
  if (!Ctx) {
    Ctx = new Context();
    if (auto Err = pthread_setspecific(CtxKey, Ctx))
      llvm_unreachable(strerror(Err));
  }
  return *reinterpret_cast<Context *>(Ctx);
}
#else
// Only supported platform without pthread is windows, and thread_local works.
static clang::clangd::Context &currentContext() {
  static thread_local auto C = clang::clangd::Context::empty();
  return C;
}
#endif

namespace clang {
namespace clangd {

Context Context::empty() { return Context(/*Data=*/nullptr); }

Context::Context(std::shared_ptr<const Data> DataPtr)
    : DataPtr(std::move(DataPtr)) {}

Context Context::clone() const { return Context(DataPtr); }

const Context &Context::current() { return currentContext(); }

Context Context::swapCurrent(Context Replacement) {
  std::swap(Replacement, currentContext());
  return Replacement;
}

} // namespace clangd
} // namespace clang
<|endoftext|>


<commit_before>// Terminal.cpp -- source file
/*
 *
 * Author: septimomend (Ivan Chapkailo)
 *
 * 30.06.2017
 *
 */

#include "stdafx.h"
#include "Terminal.h"

Terminal::Terminal() // cstr
{
}

/*
 * 0 - STDIN_FILENO - standard input descriptor
 * 1 - STDOUT_FILENO - standard output descriptor
 * 2 - STDERR_FILENO - standard errors output descriptor
 */

void Terminal::emergencyDestruction(const char* str)
{
  write(STDOUT_FILENO, "\x1b[2J", 4); // write clr code to file
  write(STDOUT_FILENO, "\x1b[H", 3);

  perror(str);
  exit(1);  // exit with error
}

void Terminal::rowModeOff()
{
  AllControllers all;
  ConfigurationController* configObj = all.getConfigObj();
  struct termios* termObj = configObj->getTermios();

  // the change will occur after all output written to STDIN_FILENO is transmitted,
  // and all input so far received but not read will be discarded before the change is made
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, termObj) == -1)
    emergencyDestruction("tcsetattr");
}

void Terminal::rowModeOn()
{
  AllControllers all;
  ConfigurationController* configObj = all.getConfigObj();
  struct termios* termObj = configObj->getTermios();

  if (tcgetattr(STDIN_FILENO, termObj) == -1)
    emergencyDestruction("tcgetattr");
  atexit(rowModeOff);

  struct termios raw = *termObj;
  // setting flags
  //
  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  raw.c_oflag &= ~(OPOST); // Post-process output
  raw.c_cflag |= (CS8); // Character size 8 bits
  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
  raw.c_cc[VMIN] = 0;
  raw.c_cc[VTIME] = 1;
}

int Terminal::whatKey() // defines key pressing
{
  int countRD;
  char c;
  while ((countRD = read(STDIN_FILENO, &c, 1)) != 1)  // read 1 byte from input device and write to c
  {
    if (countRD == -1 && errno != EAGAIN)
      emergencyDestruction("read");
  }

  if (c == '\x1b') // if this is the escape key
  {
    char sequence[3];

    if (read(STDIN_FILENO, &sequence[0], 1) != 1) // if no errors
      return '\x1b';
    if (read(STDIN_FILENO, &sequence[1], 1) != 1)
      return '\x1b';

    // this is escape-sequence
    //          \x1b[...m
    // where ... is numbers of parameters
    // more on https://en.wikipedia.org/wiki/Escape_sequences_in_C
    //
    if (sequence[0] == '[')
    {
      if (sequence[1] >= '0' && sequence[1] <= '9')
      {
        if (read(STDIN_FILENO, &sequence[2], 1) != 1)
          return '\x1b'; // if escape calls 3rd time - exit
        if (sequence[2] == '~')
        {
          switch (sequence[1])
          {
            case '1':
              return HOME_KEY;
            case '3':
              return DELETE_KEY;
            case '4':
              return END_KEY;
            case '5':
              return PAGE_UP;
            case '6':
              return PAGE_DOWN;
            case '7':
              return HOME_KEY;
            case '8':
              return END_KEY;
          }
        }
      }
      else  // else if sequence[1] is not numbers
      {
        switch (sequence[1])
        {
          case 'A':
            return ARROW_UP;
          case 'B':
            return ARROW_DOWN;
          case 'C':
            return ARROW_RIGHT;
          case 'D':
            return ARROW_LEFT;
          case 'H':
            return HOME_KEY;
          case 'F':
            return END_KEY;
        }
      }
    }
    else if (sequence[0] == 'O')
    {
      switch (sequence[1])
      {
        case 'H':
          return HOME_KEY;
        case 'F':
          return END_KEY;
      }
    }
    return '\x1b';
  }
  else
    return c;
}

int Terminal::getCursorPosition(int *row, int *column) // returns cursor position
{
  char buff[32];
  unsigned int i = 0;

  if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) // output 4 bytes to standard output device
    return -1;

  while (i < sizeof(buff) - 1)
  {
    if (read(STDIN_FILENO, &buff[i], 1) != 1) // read from ouput to buff 1 byte
      break;
    if (buff[i] == 'R')
      break;
    i++;
  }
  buff[i] = '\0';

  if (buff[0] != '\x1b' || buff[1] != '[') // if wrong data
    return -1;
  if (sscanf(&buff[2], "%d;%d", row, column) != 2) // read data from buff and check number of fields
    return -1;

  return 0;
}

int Terminal::getWindowSize(int *row, int *column) // returns size of window
{
  // TODO
}
<commit_msg>implemented func for getting window's size<commit_after>// Terminal.cpp -- source file
/*
 *
 * Author: septimomend (Ivan Chapkailo)
 *
 * 30.06.2017
 *
 */

#include "stdafx.h"
#include "Terminal.h"

Terminal::Terminal() // cstr
{
}

/*
 * 0 - STDIN_FILENO - standard input descriptor
 * 1 - STDOUT_FILENO - standard output descriptor
 * 2 - STDERR_FILENO - standard errors output descriptor
 */

void Terminal::emergencyDestruction(const char* str)
{
  write(STDOUT_FILENO, "\x1b[2J", 4); // write clr code to file
  write(STDOUT_FILENO, "\x1b[H", 3);

  perror(str);
  exit(1);  // exit with error
}

void Terminal::rowModeOff()
{
  AllControllers all;
  ConfigurationController* configObj = all.getConfigObj();
  struct termios* termObj = configObj->getTermios();

  // the change will occur after all output written to STDIN_FILENO is transmitted,
  // and all input so far received but not read will be discarded before the change is made
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, termObj) == -1)
    emergencyDestruction("tcsetattr");
}

void Terminal::rowModeOn()
{
  AllControllers all;
  ConfigurationController* configObj = all.getConfigObj();
  struct termios* termObj = configObj->getTermios();

  if (tcgetattr(STDIN_FILENO, termObj) == -1)
    emergencyDestruction("tcgetattr");
  atexit(rowModeOff);

  struct termios raw = *termObj;
  // setting flags
  //
  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  raw.c_oflag &= ~(OPOST); // Post-process output
  raw.c_cflag |= (CS8); // Character size 8 bits
  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
  raw.c_cc[VMIN] = 0;
  raw.c_cc[VTIME] = 1;
}

int Terminal::whatKey() // defines key pressing
{
  int countRD;
  char c;
  while ((countRD = read(STDIN_FILENO, &c, 1)) != 1)  // read 1 byte from input device and write to c
  {
    if (countRD == -1 && errno != EAGAIN)
      emergencyDestruction("read");
  }

  if (c == '\x1b') // if this is the escape key
  {
    char sequence[3];

    if (read(STDIN_FILENO, &sequence[0], 1) != 1) // if no errors
      return '\x1b';
    if (read(STDIN_FILENO, &sequence[1], 1) != 1)
      return '\x1b';

    // this is escape-sequence
    //          \x1b[...m
    // where ... is numbers of parameters
    // more on https://en.wikipedia.org/wiki/Escape_sequences_in_C
    //
    if (sequence[0] == '[')
    {
      if (sequence[1] >= '0' && sequence[1] <= '9')
      {
        if (read(STDIN_FILENO, &sequence[2], 1) != 1)
          return '\x1b'; // if escape calls 3rd time - exit
        if (sequence[2] == '~')
        {
          switch (sequence[1])
          {
            case '1':
              return HOME_KEY;
            case '3':
              return DELETE_KEY;
            case '4':
              return END_KEY;
            case '5':
              return PAGE_UP;
            case '6':
              return PAGE_DOWN;
            case '7':
              return HOME_KEY;
            case '8':
              return END_KEY;
          }
        }
      }
      else  // else if sequence[1] is not numbers
      {
        switch (sequence[1])
        {
          case 'A':
            return ARROW_UP;
          case 'B':
            return ARROW_DOWN;
          case 'C':
            return ARROW_RIGHT;
          case 'D':
            return ARROW_LEFT;
          case 'H':
            return HOME_KEY;
          case 'F':
            return END_KEY;
        }
      }
    }
    else if (sequence[0] == 'O')
    {
      switch (sequence[1])
      {
        case 'H':
          return HOME_KEY;
        case 'F':
          return END_KEY;
      }
    }
    return '\x1b';
  }
  else
    return c;
}

int Terminal::getCursorPosition(int *row, int *column) // returns cursor position
{
  char buff[32];
  unsigned int i = 0;

  if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) // output 4 bytes to standard output device
    return -1;

  while (i < sizeof(buff) - 1)
  {
    if (read(STDIN_FILENO, &buff[i], 1) != 1) // read from ouput to buff 1 byte
      break;
    if (buff[i] == 'R')
      break;
    i++;
  }
  buff[i] = '\0';

  if (buff[0] != '\x1b' || buff[1] != '[') // if wrong data
    return -1;
  if (sscanf(&buff[2], "%d;%d", row, column) != 2) // read data from buff and check number of fields
    return -1;

  return 0;
}

int Terminal::getWindowSize(int *row, int *column) // returns size of window
{
  struct winsize ws; // #include <sys/ioctl.h> - http://www.delorie.com/djgpp/doc/libc/libc_495.html

  if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) // do operation TIOCGWINSZ in standard output and fill
                                                                     // window's size to ws
  {
    if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12)
      return -1;
    return getCursorPosition(row, column);
  }
  else
  {
    *column = ws.ws_col;
    *row = ws.ws_row;
    return 0;
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// For information about interceptions as a whole see
// http://wiki/Main/ChromeSandboxInterceptionDesign

#include "sandbox/src/interception_agent.h"

#include "sandbox/src/interception_internal.h"
#include "sandbox/src/eat_resolver.h"
#include "sandbox/src/sidestep_resolver.h"
#include "sandbox/src/sandbox_nt_util.h"

namespace {

// Returns true if target lies between base and base + range.
bool IsWithinRange(const void* base, size_t range, const void* target) {
  const char* end = reinterpret_cast<const char*>(base) + range;
  return reinterpret_cast<const char*>(target) < end;
}

}  // namespace

namespace sandbox {

// This is the list of all imported symbols from ntdll.dll.
SANDBOX_INTERCEPT NtExports g_nt;

// Memory buffer mapped from the parent, with the list of interceptions.
SANDBOX_INTERCEPT SharedMemory* g_interceptions = NULL;

InterceptionAgent* InterceptionAgent::GetInterceptionAgent() {
  static InterceptionAgent* s_singleton_pointer = NULL;
  if (!s_singleton_pointer) {
    if (!g_interceptions)
      return NULL;

    size_t object_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*);
    s_singleton_pointer = reinterpret_cast<InterceptionAgent*>(
                              new(NT_ALLOC) char[object_bytes]);

    bool success = s_singleton_pointer->Init(g_interceptions);
    if (!success) {
      operator delete(s_singleton_pointer, NT_ALLOC);
      s_singleton_pointer = NULL;
    }
  }
  return s_singleton_pointer;
}

bool InterceptionAgent::Init(SharedMemory* shared_memory) {
  interceptions_ = shared_memory;
  for (int i = 0 ; i < shared_memory->num_intercepted_dlls; i++)
    dlls_[i] = NULL;
  return true;
}

bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path,
                                 const UNICODE_STRING* name,
                                 const DllPatchInfo* dll_info) {
  UNICODE_STRING current_name;
  current_name.Length = static_cast<USHORT>(g_nt.wcslen(dll_info->dll_name) *
                                            sizeof(wchar_t));
  current_name.MaximumLength = current_name.Length;
  current_name.Buffer = const_cast<wchar_t*>(dll_info->dll_name);

  BOOLEAN case_insensitive = TRUE;
  if (full_path &&
      !g_nt.RtlCompareUnicodeString(&current_name, full_path, case_insensitive))
    return true;

  if (!g_nt.RtlCompareUnicodeString(&current_name, name, case_insensitive))
    return true;

  return false;
}

void InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
                                  const UNICODE_STRING* name,
                                  void* base_address) {
  DllPatchInfo* dll_info = interceptions_->dll_list;
  int i = 0;
  for (; i < interceptions_->num_intercepted_dlls; i++) {
    if (DllMatch(full_path, name, dll_info))
      break;

    dll_info = reinterpret_cast<DllPatchInfo*>(
                   reinterpret_cast<char*>(dll_info) + dll_info->record_bytes);
  }
  if (i == interceptions_->num_intercepted_dlls)
    return;

  // Purify causes this condition to trigger.
  if (dlls_[i])
    return;

  size_t buffer_bytes = offsetof(DllInterceptionData, thunks) +
                        dll_info->num_functions * sizeof(ThunkData);
  dlls_[i] = reinterpret_cast<DllInterceptionData*>(
                 new(NT_PAGE) char[buffer_bytes]);

  DCHECK_NT(dlls_[i]);
  if (!dlls_[i])
    return;

  dlls_[i]->data_bytes = buffer_bytes;
  dlls_[i]->num_thunks = 0;
  dlls_[i]->base = base_address;
  dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks);

  VERIFY(PatchDll(dll_info, dlls_[i]));

  ULONG old_protect;
  SIZE_T real_size = buffer_bytes;
  void* to_protect = dlls_[i];
  VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect,
                                           &real_size, PAGE_EXECUTE_READ,
                                           &old_protect));
}

void InterceptionAgent::OnDllUnload(void* base_address) {
  for (int i = 0; i < interceptions_->num_intercepted_dlls; i++) {
    if (dlls_[i] && dlls_[i]->base == base_address) {
      operator delete(dlls_[i], NT_PAGE);
      dlls_[i] = NULL;
      break;
    }
  }
}

// TODO(rvargas): We have to deal with prebinded dlls. I see two options: change
// the timestamp of the patched dll, or modify the info on the prebinded dll.
// the first approach messes matching of debug symbols, the second one is more
// complicated.
bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info,
                                 DllInterceptionData* thunks) {
  DCHECK_NT(NULL != thunks);
  DCHECK_NT(NULL != dll_info);

  const FunctionInfo* function = reinterpret_cast<const FunctionInfo*>(
      reinterpret_cast<const char*>(dll_info) + dll_info->offset_to_functions);

  for (int i = 0; i < dll_info->num_functions; i++) {
    if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) {
      NOTREACHED_NT();
      return false;
    }

    ResolverThunk* resolver = GetResolver(function->type);
    if (!resolver)
      return false;

    const char* interceptor = function->function +
                              g_nt.strlen(function->function) + 1;

    if (!IsWithinRange(function, function->record_bytes, interceptor) ||
        !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) {
      NOTREACHED_NT();
      return false;
    }

    NTSTATUS ret = resolver->Setup(thunks->base,
                                   interceptions_->interceptor_base,
                                   function->function,
                                   interceptor,
                                   function->interceptor_address,
                                   &thunks->thunks[i],
                                   sizeof(ThunkData),
                                   NULL);
    if (!NT_SUCCESS(ret)) {
      NOTREACHED_NT();
      return false;
    }

    thunks->num_thunks++;
    thunks->used_bytes += sizeof(ThunkData);

    function = reinterpret_cast<const FunctionInfo*>(
        reinterpret_cast<const char*>(function) + function->record_bytes);
  }

  return true;
}

// This method is called from within the loader lock
ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) {
  static EatResolverThunk* eat_resolver = NULL;
  static SidestepResolverThunk* sidestep_resolver = NULL;
  static SmartSidestepResolverThunk* smart_sidestep_resolver = NULL;

  if (!eat_resolver)
    eat_resolver = new(NT_ALLOC) EatResolverThunk;

  if (!sidestep_resolver)
    sidestep_resolver = new(NT_ALLOC) SidestepResolverThunk;

  if (!smart_sidestep_resolver)
    smart_sidestep_resolver = new(NT_ALLOC) SmartSidestepResolverThunk;

  switch (type) {
    case INTERCEPTION_EAT:
      return eat_resolver;
    case INTERCEPTION_SIDESTEP:
      return sidestep_resolver;
    case INTERCEPTION_SMART_SIDESTEP:
      return smart_sidestep_resolver;
    default:
      NOTREACHED_NT();
  }

  return NULL;
}

}  // namespace sandbox

<commit_msg>Fix memory corruption when EAT patching in sandbox<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// For information about interceptions as a whole see
// http://wiki/Main/ChromeSandboxInterceptionDesign

#include "sandbox/src/interception_agent.h"

#include "sandbox/src/interception_internal.h"
#include "sandbox/src/eat_resolver.h"
#include "sandbox/src/sidestep_resolver.h"
#include "sandbox/src/sandbox_nt_util.h"

namespace {

// Returns true if target lies between base and base + range.
bool IsWithinRange(const void* base, size_t range, const void* target) {
  const char* end = reinterpret_cast<const char*>(base) + range;
  return reinterpret_cast<const char*>(target) < end;
}

}  // namespace

namespace sandbox {

// This is the list of all imported symbols from ntdll.dll.
SANDBOX_INTERCEPT NtExports g_nt;

// Memory buffer mapped from the parent, with the list of interceptions.
SANDBOX_INTERCEPT SharedMemory* g_interceptions = NULL;

InterceptionAgent* InterceptionAgent::GetInterceptionAgent() {
  static InterceptionAgent* s_singleton = NULL;
  if (!s_singleton) {
    if (!g_interceptions)
      return NULL;

    size_t array_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*);
    s_singleton = reinterpret_cast<InterceptionAgent*>(
        new(NT_ALLOC) char[array_bytes + sizeof(InterceptionAgent)]);

    bool success = s_singleton->Init(g_interceptions);
    if (!success) {
      operator delete(s_singleton, NT_ALLOC);
      s_singleton = NULL;
    }
  }
  return s_singleton;
}

bool InterceptionAgent::Init(SharedMemory* shared_memory) {
  interceptions_ = shared_memory;
  for (int i = 0 ; i < shared_memory->num_intercepted_dlls; i++)
    dlls_[i] = NULL;
  return true;
}

bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path,
                                 const UNICODE_STRING* name,
                                 const DllPatchInfo* dll_info) {
  UNICODE_STRING current_name;
  current_name.Length = static_cast<USHORT>(g_nt.wcslen(dll_info->dll_name) *
                                            sizeof(wchar_t));
  current_name.MaximumLength = current_name.Length;
  current_name.Buffer = const_cast<wchar_t*>(dll_info->dll_name);

  BOOLEAN case_insensitive = TRUE;
  if (full_path &&
      !g_nt.RtlCompareUnicodeString(&current_name, full_path, case_insensitive))
    return true;

  if (!g_nt.RtlCompareUnicodeString(&current_name, name, case_insensitive))
    return true;

  return false;
}

void InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
                                  const UNICODE_STRING* name,
                                  void* base_address) {
  DllPatchInfo* dll_info = interceptions_->dll_list;
  int i = 0;
  for (; i < interceptions_->num_intercepted_dlls; i++) {
    if (DllMatch(full_path, name, dll_info))
      break;

    dll_info = reinterpret_cast<DllPatchInfo*>(
                   reinterpret_cast<char*>(dll_info) + dll_info->record_bytes);
  }
  if (i == interceptions_->num_intercepted_dlls)
    return;

  // Purify causes this condition to trigger.
  if (dlls_[i])
    return;

  size_t buffer_bytes = offsetof(DllInterceptionData, thunks) +
                        dll_info->num_functions * sizeof(ThunkData);
  dlls_[i] = reinterpret_cast<DllInterceptionData*>(
                 new(NT_PAGE) char[buffer_bytes]);

  DCHECK_NT(dlls_[i]);
  if (!dlls_[i])
    return;

  dlls_[i]->data_bytes = buffer_bytes;
  dlls_[i]->num_thunks = 0;
  dlls_[i]->base = base_address;
  dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks);

  VERIFY(PatchDll(dll_info, dlls_[i]));

  ULONG old_protect;
  SIZE_T real_size = buffer_bytes;
  void* to_protect = dlls_[i];
  VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect,
                                           &real_size, PAGE_EXECUTE_READ,
                                           &old_protect));
}

void InterceptionAgent::OnDllUnload(void* base_address) {
  for (int i = 0; i < interceptions_->num_intercepted_dlls; i++) {
    if (dlls_[i] && dlls_[i]->base == base_address) {
      operator delete(dlls_[i], NT_PAGE);
      dlls_[i] = NULL;
      break;
    }
  }
}

// TODO(rvargas): We have to deal with prebinded dlls. I see two options: change
// the timestamp of the patched dll, or modify the info on the prebinded dll.
// the first approach messes matching of debug symbols, the second one is more
// complicated.
bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info,
                                 DllInterceptionData* thunks) {
  DCHECK_NT(NULL != thunks);
  DCHECK_NT(NULL != dll_info);

  const FunctionInfo* function = reinterpret_cast<const FunctionInfo*>(
      reinterpret_cast<const char*>(dll_info) + dll_info->offset_to_functions);

  for (int i = 0; i < dll_info->num_functions; i++) {
    if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) {
      NOTREACHED_NT();
      return false;
    }

    ResolverThunk* resolver = GetResolver(function->type);
    if (!resolver)
      return false;

    const char* interceptor = function->function +
                              g_nt.strlen(function->function) + 1;

    if (!IsWithinRange(function, function->record_bytes, interceptor) ||
        !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) {
      NOTREACHED_NT();
      return false;
    }

    NTSTATUS ret = resolver->Setup(thunks->base,
                                   interceptions_->interceptor_base,
                                   function->function,
                                   interceptor,
                                   function->interceptor_address,
                                   &thunks->thunks[i],
                                   sizeof(ThunkData),
                                   NULL);
    if (!NT_SUCCESS(ret)) {
      NOTREACHED_NT();
      return false;
    }

    thunks->num_thunks++;
    thunks->used_bytes += sizeof(ThunkData);

    function = reinterpret_cast<const FunctionInfo*>(
        reinterpret_cast<const char*>(function) + function->record_bytes);
  }

  return true;
}

// This method is called from within the loader lock
ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) {
  static EatResolverThunk* eat_resolver = NULL;
  static SidestepResolverThunk* sidestep_resolver = NULL;
  static SmartSidestepResolverThunk* smart_sidestep_resolver = NULL;

  if (!eat_resolver)
    eat_resolver = new(NT_ALLOC) EatResolverThunk;

  if (!sidestep_resolver)
    sidestep_resolver = new(NT_ALLOC) SidestepResolverThunk;

  if (!smart_sidestep_resolver)
    smart_sidestep_resolver = new(NT_ALLOC) SmartSidestepResolverThunk;

  switch (type) {
    case INTERCEPTION_EAT:
      return eat_resolver;
    case INTERCEPTION_SIDESTEP:
      return sidestep_resolver;
    case INTERCEPTION_SMART_SIDESTEP:
      return smart_sidestep_resolver;
    default:
      NOTREACHED_NT();
  }

  return NULL;
}

}  // namespace sandbox

<|endoftext|>


<commit_before>
/*
 * 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 "gm.h"
#include "SkGradientShader.h"

namespace skiagm {

struct GradData {
    int             fCount;
    const SkColor*  fColors;
    const SkScalar* fPos;
};

static const SkColor gColors[] = {
    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
};
static const SkScalar gPos0[] = { 0, SK_Scalar1 };
static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
static const SkScalar gPos2[] = {
    0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
};

static const GradData gGradData[] = {
    { 2, gColors, NULL },
    { 2, gColors, gPos0 },
    { 2, gColors, gPos1 },
    { 5, gColors, NULL },
    { 5, gColors, gPos2 }
};

static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
                            SkShader::TileMode tm, SkUnitMapper* mapper) {
    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
                                          data.fCount, tm, mapper);
}

static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
                            SkShader::TileMode tm, SkUnitMapper* mapper) {
    SkPoint center;
    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
               SkScalarAve(pts[0].fY, pts[1].fY));
    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
                                          data.fPos, data.fCount, tm, mapper);
}

static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
                           SkShader::TileMode tm, SkUnitMapper* mapper) {
    SkPoint center;
    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
               SkScalarAve(pts[0].fY, pts[1].fY));
    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
                                         data.fPos, data.fCount, mapper);
}

static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
                             SkShader::TileMode tm, SkUnitMapper* mapper) {
    SkPoint center0, center1;
    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
                SkScalarAve(pts[0].fY, pts[1].fY));
    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
    return SkGradientShader::CreateTwoPointRadial(
                                                  center1, (pts[1].fX - pts[0].fX) / 7,
                                                  center0, (pts[1].fX - pts[0].fX) / 2,
                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
}

typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
                               SkShader::TileMode tm, SkUnitMapper* mapper);
static const GradMaker gGradMakers[] = {
    MakeLinear, MakeRadial, MakeSweep, Make2Radial
};

///////////////////////////////////////////////////////////////////////////////

class GradientsGM : public GM {
public:
	GradientsGM() {
        this->setBGColor(0xFFDDDDDD);
    }

protected:
    SkString onShortName() {
        return SkString("gradients");
    }

    virtual SkISize onISize() { return make_isize(640, 510); }

    virtual void onDraw(SkCanvas* canvas) {

        SkPoint pts[2] = {
            { 0, 0 },
            { SkIntToScalar(100), SkIntToScalar(100) }
        };
        SkShader::TileMode tm = SkShader::kClamp_TileMode;
        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
        SkPaint paint;
        paint.setAntiAlias(true);

        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
        for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
            canvas->save();
            for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
                SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL);
                paint.setShader(shader);
                canvas->drawRect(r, paint);
                shader->unref();
                canvas->translate(0, SkIntToScalar(120));
            }
            canvas->restore();
            canvas->translate(SkIntToScalar(120), 0);
        }
    }

private:
    typedef GM INHERITED;
};

/*
 Inspired by this <canvas> javascript, where we need to detect that we are not
 solving a quadratic equation, but must instead solve a linear (since our X^2
 coefficient is 0)

 ctx.fillStyle = '#f00';
 ctx.fillRect(0, 0, 100, 50);

 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
 g.addColorStop(0, '#f00');
 g.addColorStop(0.01, '#0f0');
 g.addColorStop(0.99, '#0f0');
 g.addColorStop(1, '#f00');
 ctx.fillStyle = g;
 ctx.fillRect(0, 0, 100, 50);
 */
class GradientsDegenrate2PointGM : public GM {
public:
    GradientsDegenrate2PointGM() {}

protected:
    SkString onShortName() {
        return SkString("gradients_degenerate_2pt");
    }

	virtual SkISize onISize() { return make_isize(320, 320); }

    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(SK_ColorBLUE);
    }

    virtual void onDraw(SkCanvas* canvas) {
        this->drawBG(canvas);

        SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
        SkScalar pos[] = { 0, SkFloatToScalar(0.01f), SkFloatToScalar(0.99f), SK_Scalar1 };
        SkPoint c0;
        c0.iset(-80, 25);
        SkScalar r0 = SkIntToScalar(70);
        SkPoint c1;
        c1.iset(0, 25);
        SkScalar r1 = SkIntToScalar(150);
        SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
                                                             pos, SK_ARRAY_COUNT(pos),
                                                             SkShader::kClamp_TileMode);
        SkPaint paint;
        paint.setShader(s)->unref();
        canvas->drawPaint(paint);
    }

private:
    typedef GM INHERITED;
};

/// Tests correctness of *optimized* codepaths in gradients.

class ClampedGradientsGM : public GM {
public:
    ClampedGradientsGM() {}

protected:
    SkString onShortName() { return SkString("clamped_gradients"); }

    virtual SkISize onISize() { return make_isize(640, 510); }

    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFFDDDDDD);
    }

    virtual void onDraw(SkCanvas* canvas) {
        this->drawBG(canvas);

        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
        SkPaint paint;
        paint.setAntiAlias(true);

        SkPoint center;
        center.iset(0, 300);
        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
        SkShader* shader = SkGradientShader::CreateRadial(
            SkPoint(center),
            SkIntToScalar(200), gColors, NULL, 5,
            SkShader::kClamp_TileMode, NULL);
        paint.setShader(shader);
        canvas->drawRect(r, paint);
        shader->unref();
    }

private:
    typedef GM INHERITED;
};

/// Checks quality of large radial gradients, which may display
/// some banding.

class RadialGradientGM : public GM {
public:
    RadialGradientGM() {}

protected:
    SkString onShortName() { return SkString("radial_gradient"); }
    virtual SkISize onISize() { return make_isize(1280, 1280); }
    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFF000000);
    }
    virtual void onDraw(SkCanvas* canvas) {
        const SkISize dim = this->getISize();

        this->drawBG(canvas);

        SkPaint paint;
        paint.setDither(true);
        SkPoint center;
        center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
        SkScalar radius = SkIntToScalar(dim.width())/2;
        const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
        const SkScalar pos[] = { SkFloatToScalar(0.0f),
                             SkFloatToScalar(0.35f),
                             SkFloatToScalar(1.0f) };
        SkShader* shader =
            SkGradientShader::CreateRadial(center, radius, colors,
                                           pos, SK_ARRAY_COUNT(pos),
                                           SkShader::kClamp_TileMode);
        paint.setShader(shader)->unref();
        SkRect r = {
            0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
        };
        canvas->drawRect(r, paint);
    }
private:
    typedef GM INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

static GM* MyFactory(void*) { return new GradientsGM; }
static GMRegistry reg(MyFactory);

static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
static GMRegistry reg2(MyFactory2);

static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
static GMRegistry reg3(MyFactory3);

static GM* MyFactory4(void*) { return new RadialGradientGM; }
static GMRegistry reg4(MyFactory4);
}

<commit_msg>Added conical gradient to gm gradients test, and added two variants of that test, one with perspective applied to the local matrices of the gradients and the other with perspective applied to the overall view matrix.<commit_after>
/*
 * 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 "gm.h"
#include "SkGradientShader.h"

namespace skiagm {

struct GradData {
    int             fCount;
    const SkColor*  fColors;
    const SkScalar* fPos;
};

static const SkColor gColors[] = {
    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
};
static const SkScalar gPos0[] = { 0, SK_Scalar1 };
static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
static const SkScalar gPos2[] = {
    0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
};

static const GradData gGradData[] = {
    { 2, gColors, NULL },
    { 2, gColors, gPos0 },
    { 2, gColors, gPos1 },
    { 5, gColors, NULL },
    { 5, gColors, gPos2 }
};

static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
                            SkShader::TileMode tm, SkUnitMapper* mapper) {
    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
                                          data.fCount, tm, mapper);
}

static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
                            SkShader::TileMode tm, SkUnitMapper* mapper) {
    SkPoint center;
    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
               SkScalarAve(pts[0].fY, pts[1].fY));
    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
                                          data.fPos, data.fCount, tm, mapper);
}

static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
                           SkShader::TileMode tm, SkUnitMapper* mapper) {
    SkPoint center;
    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
               SkScalarAve(pts[0].fY, pts[1].fY));
    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
                                         data.fPos, data.fCount, mapper);
}

static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
                             SkShader::TileMode tm, SkUnitMapper* mapper) {
    SkPoint center0, center1;
    center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
                SkScalarAve(pts[0].fY, pts[1].fY));
    center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
                SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
    return SkGradientShader::CreateTwoPointRadial(
                                                  center1, (pts[1].fX - pts[0].fX) / 7,
                                                  center0, (pts[1].fX - pts[0].fX) / 2,
                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
}

static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data,
                             SkShader::TileMode tm, SkUnitMapper* mapper) {
    SkPoint center0, center1;
    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
    SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
    center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
    center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
    return SkGradientShader::CreateTwoPointConical(center1, radius1,
                                                   center0, radius0,
                                                   data.fColors, data.fPos,
                                                   data.fCount, tm, mapper);
}

typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
                               SkShader::TileMode tm, SkUnitMapper* mapper);
static const GradMaker gGradMakers[] = {
    MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
};

///////////////////////////////////////////////////////////////////////////////

class GradientsGM : public GM {
public:
	GradientsGM() {
        this->setBGColor(0xFFDDDDDD);
    }

protected:
    SkString onShortName() {
        return SkString("gradients");
    }

    virtual SkISize onISize() { return make_isize(640, 615); }

    virtual void onDraw(SkCanvas* canvas) {

        SkPoint pts[2] = {
            { 0, 0 },
            { SkIntToScalar(100), SkIntToScalar(100) }
        };
        SkShader::TileMode tm = SkShader::kClamp_TileMode;
        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
        SkPaint paint;
        paint.setAntiAlias(true);

        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
        for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
            canvas->save();
            for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
                SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL);
                paint.setShader(shader);
                canvas->drawRect(r, paint);
                shader->unref();
                canvas->translate(0, SkIntToScalar(120));
            }
            canvas->restore();
            canvas->translate(SkIntToScalar(120), 0);
        }
    }

private:
    typedef GM INHERITED;
};

// Based on the original gradient slide, but with perspective applied to the
// gradient shaders' local matrices
class GradientsLocalPerspectiveGM : public GM {
public:
	GradientsLocalPerspectiveGM() {
        this->setBGColor(0xFFDDDDDD);
    }

protected:
    SkString onShortName() {
        return SkString("gradients_local_perspective");
    }

    virtual SkISize onISize() { return make_isize(640, 615); }

    virtual void onDraw(SkCanvas* canvas) {

        SkPoint pts[2] = {
            { 0, 0 },
            { SkIntToScalar(100), SkIntToScalar(100) }
        };
        SkShader::TileMode tm = SkShader::kClamp_TileMode;
        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
        SkPaint paint;
        paint.setAntiAlias(true);

        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
        for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
            canvas->save();
            for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
                SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL);

                // apply an increasing y perspective as we move to the right
                SkMatrix perspective;
                perspective.setIdentity();
                perspective.setPerspY(SkScalarDiv(SkIntToScalar(i+1),
                                      SkIntToScalar(500)));
                perspective.setSkewX(SkScalarDiv(SkIntToScalar(i+1),
                                     SkIntToScalar(10)));
                shader->setLocalMatrix(perspective);

                paint.setShader(shader);
                canvas->drawRect(r, paint);
                shader->unref();
                canvas->translate(0, SkIntToScalar(120));
            }
            canvas->restore();
            canvas->translate(SkIntToScalar(120), 0);
        }
    }

private:
    typedef GM INHERITED;
};

// Based on the original gradient slide, but with perspective applied to
// the view matrix
class GradientsViewPerspectiveGM : public GradientsGM {
protected:
    SkString onShortName() {
        return SkString("gradients_view_perspective");
    }

    virtual SkISize onISize() { return make_isize(640, 400); }

    virtual void onDraw(SkCanvas* canvas) {
        SkMatrix perspective;
        perspective.setIdentity();
        perspective.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000)));
        perspective.setSkewX(SkScalarDiv(SkIntToScalar(8), SkIntToScalar(25)));
        canvas->setMatrix(perspective);
        INHERITED::onDraw(canvas);
    }

private:
    typedef GradientsGM INHERITED;
};

/*
 Inspired by this <canvas> javascript, where we need to detect that we are not
 solving a quadratic equation, but must instead solve a linear (since our X^2
 coefficient is 0)

 ctx.fillStyle = '#f00';
 ctx.fillRect(0, 0, 100, 50);

 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
 g.addColorStop(0, '#f00');
 g.addColorStop(0.01, '#0f0');
 g.addColorStop(0.99, '#0f0');
 g.addColorStop(1, '#f00');
 ctx.fillStyle = g;
 ctx.fillRect(0, 0, 100, 50);
 */
class GradientsDegenrate2PointGM : public GM {
public:
    GradientsDegenrate2PointGM() {}

protected:
    SkString onShortName() {
        return SkString("gradients_degenerate_2pt");
    }

	virtual SkISize onISize() { return make_isize(320, 320); }

    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(SK_ColorBLUE);
    }

    virtual void onDraw(SkCanvas* canvas) {
        this->drawBG(canvas);

        SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
        SkScalar pos[] = { 0, SkFloatToScalar(0.01f), SkFloatToScalar(0.99f), SK_Scalar1 };
        SkPoint c0;
        c0.iset(-80, 25);
        SkScalar r0 = SkIntToScalar(70);
        SkPoint c1;
        c1.iset(0, 25);
        SkScalar r1 = SkIntToScalar(150);
        SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
                                                             pos, SK_ARRAY_COUNT(pos),
                                                             SkShader::kClamp_TileMode);
        SkPaint paint;
        paint.setShader(s)->unref();
        canvas->drawPaint(paint);
    }

private:
    typedef GM INHERITED;
};

/// Tests correctness of *optimized* codepaths in gradients.

class ClampedGradientsGM : public GM {
public:
    ClampedGradientsGM() {}

protected:
    SkString onShortName() { return SkString("clamped_gradients"); }

    virtual SkISize onISize() { return make_isize(640, 510); }

    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFFDDDDDD);
    }

    virtual void onDraw(SkCanvas* canvas) {
        this->drawBG(canvas);

        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
        SkPaint paint;
        paint.setAntiAlias(true);

        SkPoint center;
        center.iset(0, 300);
        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
        SkShader* shader = SkGradientShader::CreateRadial(
            SkPoint(center),
            SkIntToScalar(200), gColors, NULL, 5,
            SkShader::kClamp_TileMode, NULL);
        paint.setShader(shader);
        canvas->drawRect(r, paint);
        shader->unref();
    }

private:
    typedef GM INHERITED;
};

/// Checks quality of large radial gradients, which may display
/// some banding.

class RadialGradientGM : public GM {
public:
    RadialGradientGM() {}

protected:
    SkString onShortName() { return SkString("radial_gradient"); }
    virtual SkISize onISize() { return make_isize(1280, 1280); }
    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFF000000);
    }
    virtual void onDraw(SkCanvas* canvas) {
        const SkISize dim = this->getISize();

        this->drawBG(canvas);

        SkPaint paint;
        paint.setDither(true);
        SkPoint center;
        center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
        SkScalar radius = SkIntToScalar(dim.width())/2;
        const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
        const SkScalar pos[] = { SkFloatToScalar(0.0f),
                             SkFloatToScalar(0.35f),
                             SkFloatToScalar(1.0f) };
        SkShader* shader =
            SkGradientShader::CreateRadial(center, radius, colors,
                                           pos, SK_ARRAY_COUNT(pos),
                                           SkShader::kClamp_TileMode);
        paint.setShader(shader)->unref();
        SkRect r = {
            0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
        };
        canvas->drawRect(r, paint);
    }
private:
    typedef GM INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

static GM* MyFactory(void*) { return new GradientsGM; }
static GMRegistry reg(MyFactory);

static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
static GMRegistry reg2(MyFactory2);

static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
static GMRegistry reg3(MyFactory3);

static GM* MyFactory4(void*) { return new RadialGradientGM; }
static GMRegistry reg4(MyFactory4);

static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; }
static GMRegistry reg5(MyFactory5);

static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; }
static GMRegistry reg6(MyFactory6);
}

<|endoftext|>