<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2020 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#include <realm/util/to_string.hpp>

#include <atomic>
#include <dispatch/dispatch.h>
#include <objc/runtime.h>
#include <pthread.h>

namespace {
using namespace realm;

class RunLoopScheduler : public util::Scheduler {
public:
    RunLoopScheduler(CFRunLoopRef run_loop = nullptr);
    ~RunLoopScheduler();

    void notify() override;
    void set_notify_callback(std::function<void()>) override;

    bool is_on_thread() const noexcept override;
    bool is_same_as(const Scheduler* other) const noexcept override;
    bool can_deliver_notifications() const noexcept override;

private:
    CFRunLoopRef m_runloop;
    CFRunLoopSourceRef m_signal = nullptr;
};

RunLoopScheduler::RunLoopScheduler(CFRunLoopRef run_loop)
: m_runloop(run_loop ?: CFRunLoopGetCurrent())
{
    CFRetain(m_runloop);
}

RunLoopScheduler::~RunLoopScheduler()
{
    if (m_signal) {
        CFRunLoopSourceInvalidate(m_signal);
        CFRelease(m_signal);
    }
    CFRelease(m_runloop);
}

void RunLoopScheduler::set_notify_callback(std::function<void ()> callback)
{
    if (m_signal) {
        CFRunLoopSourceInvalidate(m_signal);
        CFRelease(m_signal);
        m_signal = nullptr;
    }

    struct RefCountedRunloopCallback {
        std::function<void()> callback;
        std::atomic<size_t> ref_count;
    };

    CFRunLoopSourceContext ctx{};
    ctx.info = new RefCountedRunloopCallback{std::move(callback), {0}};
    ctx.perform = [](void* info) {
        static_cast<RefCountedRunloopCallback*>(info)->callback();
    };
    ctx.retain = [](const void* info) {
        static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info))->ref_count.fetch_add(1, std::memory_order_relaxed);
        return info;
    };
    ctx.release = [](const void* info) {
        auto ptr = static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info));
        if (ptr->ref_count.fetch_add(-1, std::memory_order_acq_rel) == 1) {
            delete ptr;
        }
    };

    m_signal = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &ctx);
    CFRunLoopAddSource(m_runloop, m_signal, kCFRunLoopDefaultMode);
}

void RunLoopScheduler::notify()
{
    if (!m_signal)
        return;

    CFRunLoopSourceSignal(m_signal);
    // Signalling the source makes it run the next time the runloop gets
    // to it, but doesn't make the runloop start if it's currently idle
    // waiting for events
    CFRunLoopWakeUp(m_runloop);
}

bool RunLoopScheduler::is_on_thread() const noexcept
{
    return CFRunLoopGetCurrent() == m_runloop;
}

bool RunLoopScheduler::is_same_as(const Scheduler* other) const noexcept
{
    auto o = dynamic_cast<const RunLoopScheduler*>(other);
    return (o && (o->m_runloop == m_runloop));
}

bool RunLoopScheduler::can_deliver_notifications() const noexcept
{
    // The main thread may not be in a run loop yet if we're called from
    // something like `applicationDidFinishLaunching:`, but it presumably will
    // be in the future
    if (pthread_main_np())
        return true;

    // Current mode indicates why the current callout from the runloop was made,
    // and is null if a runloop callout isn't currently being processed
    if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) {
        CFRelease(mode);
        return true;
    }
    return false;
}

class DispatchQueueScheduler : public util::Scheduler {
public:
    DispatchQueueScheduler(dispatch_queue_t queue);
    ~DispatchQueueScheduler();

    void notify() override;
    void set_notify_callback(std::function<void()>) override;

    bool is_on_thread() const noexcept override;
    bool is_same_as(const Scheduler* other) const noexcept override;
    bool can_deliver_notifications() const noexcept override { return true; }

private:
    dispatch_queue_t m_queue = nullptr;
    void (^m_callback)() = nullptr;
};

static const void* c_queue_key = &c_queue_key;

DispatchQueueScheduler::DispatchQueueScheduler(dispatch_queue_t queue)
: m_queue(queue)
{
    static auto class_dispatch_queue_serial = objc_getClass("OS_dispatch_queue_serial");
    static auto class_dispatch_queue_main = objc_getClass("OS_dispatch_queue_main");
    auto cls = object_getClass(reinterpret_cast<id>(queue));
    if (cls != class_dispatch_queue_serial && cls != class_dispatch_queue_main) {
        auto msg = util::format("Invalid queue '%1' (%2): Realms can only be confined to serial queues or the main queue.",
                                dispatch_queue_get_label(queue) ?: "<nil>", class_getName(cls));
        throw std::logic_error(msg);
    }
    dispatch_retain(m_queue);
    if (dispatch_queue_get_specific(m_queue, c_queue_key) == nullptr) {
        dispatch_queue_set_specific(m_queue, c_queue_key, queue, nullptr);
    }
}

DispatchQueueScheduler::~DispatchQueueScheduler()
{
    dispatch_release(m_queue);
    if (m_callback)
        Block_release(m_callback);
}

void DispatchQueueScheduler::notify()
{
    dispatch_async(m_queue, m_callback);
}

void DispatchQueueScheduler::set_notify_callback(std::function<void ()> callback)
{
    m_callback = Block_copy(^{
        callback();
    });
}

bool DispatchQueueScheduler::is_on_thread() const noexcept
{
    return dispatch_get_specific(c_queue_key) == m_queue;
}

bool DispatchQueueScheduler::is_same_as(const Scheduler* other) const noexcept
{
    auto o = dynamic_cast<const DispatchQueueScheduler*>(other);
    return (o && (o->m_queue == m_queue));
}

} // anonymous namespace

namespace realm {
namespace util {
std::shared_ptr<Scheduler> Scheduler::make_default()
{
    return std::make_shared<RunLoopScheduler>();
}

std::shared_ptr<Scheduler> Scheduler::make_runloop(CFRunLoopRef run_loop)
{
    return std::make_shared<RunLoopScheduler>(run_loop ?: CFRunLoopGetCurrent());
}

std::shared_ptr<Scheduler> Scheduler::make_dispatch(void* queue)
{
    return std::make_shared<DispatchQueueScheduler>(static_cast<dispatch_queue_t>(queue));
}
} // namespace util
} // namespace realm
<commit_msg>Skip validating dispatch queue types on older OSes<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2020 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#include <realm/util/to_string.hpp>

#include <atomic>
#include <dispatch/dispatch.h>
#include <objc/runtime.h>
#include <pthread.h>

namespace {
using namespace realm;

class RunLoopScheduler : public util::Scheduler {
public:
    RunLoopScheduler(CFRunLoopRef run_loop = nullptr);
    ~RunLoopScheduler();

    void notify() override;
    void set_notify_callback(std::function<void()>) override;

    bool is_on_thread() const noexcept override;
    bool is_same_as(const Scheduler* other) const noexcept override;
    bool can_deliver_notifications() const noexcept override;

private:
    CFRunLoopRef m_runloop;
    CFRunLoopSourceRef m_signal = nullptr;
};

RunLoopScheduler::RunLoopScheduler(CFRunLoopRef run_loop)
: m_runloop(run_loop ?: CFRunLoopGetCurrent())
{
    CFRetain(m_runloop);
}

RunLoopScheduler::~RunLoopScheduler()
{
    if (m_signal) {
        CFRunLoopSourceInvalidate(m_signal);
        CFRelease(m_signal);
    }
    CFRelease(m_runloop);
}

void RunLoopScheduler::set_notify_callback(std::function<void ()> callback)
{
    if (m_signal) {
        CFRunLoopSourceInvalidate(m_signal);
        CFRelease(m_signal);
        m_signal = nullptr;
    }

    struct RefCountedRunloopCallback {
        std::function<void()> callback;
        std::atomic<size_t> ref_count;
    };

    CFRunLoopSourceContext ctx{};
    ctx.info = new RefCountedRunloopCallback{std::move(callback), {0}};
    ctx.perform = [](void* info) {
        static_cast<RefCountedRunloopCallback*>(info)->callback();
    };
    ctx.retain = [](const void* info) {
        static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info))->ref_count.fetch_add(1, std::memory_order_relaxed);
        return info;
    };
    ctx.release = [](const void* info) {
        auto ptr = static_cast<RefCountedRunloopCallback*>(const_cast<void*>(info));
        if (ptr->ref_count.fetch_add(-1, std::memory_order_acq_rel) == 1) {
            delete ptr;
        }
    };

    m_signal = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &ctx);
    CFRunLoopAddSource(m_runloop, m_signal, kCFRunLoopDefaultMode);
}

void RunLoopScheduler::notify()
{
    if (!m_signal)
        return;

    CFRunLoopSourceSignal(m_signal);
    // Signalling the source makes it run the next time the runloop gets
    // to it, but doesn't make the runloop start if it's currently idle
    // waiting for events
    CFRunLoopWakeUp(m_runloop);
}

bool RunLoopScheduler::is_on_thread() const noexcept
{
    return CFRunLoopGetCurrent() == m_runloop;
}

bool RunLoopScheduler::is_same_as(const Scheduler* other) const noexcept
{
    auto o = dynamic_cast<const RunLoopScheduler*>(other);
    return (o && (o->m_runloop == m_runloop));
}

bool RunLoopScheduler::can_deliver_notifications() const noexcept
{
    // The main thread may not be in a run loop yet if we're called from
    // something like `applicationDidFinishLaunching:`, but it presumably will
    // be in the future
    if (pthread_main_np())
        return true;

    // Current mode indicates why the current callout from the runloop was made,
    // and is null if a runloop callout isn't currently being processed
    if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) {
        CFRelease(mode);
        return true;
    }
    return false;
}

class DispatchQueueScheduler : public util::Scheduler {
public:
    DispatchQueueScheduler(dispatch_queue_t queue);
    ~DispatchQueueScheduler();

    void notify() override;
    void set_notify_callback(std::function<void()>) override;

    bool is_on_thread() const noexcept override;
    bool is_same_as(const Scheduler* other) const noexcept override;
    bool can_deliver_notifications() const noexcept override { return true; }

private:
    dispatch_queue_t m_queue = nullptr;
    void (^m_callback)() = nullptr;
};

static const void* c_queue_key = &c_queue_key;

DispatchQueueScheduler::DispatchQueueScheduler(dispatch_queue_t queue)
: m_queue(queue)
{
    if (__builtin_available(iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 5.0, *)) {
        static auto class_dispatch_queue_serial = objc_getClass("OS_dispatch_queue_serial");
        static auto class_dispatch_queue_main = objc_getClass("OS_dispatch_queue_main");
        auto cls = object_getClass(reinterpret_cast<id>(queue));
        if (cls != class_dispatch_queue_serial && cls != class_dispatch_queue_main) {
            auto msg = util::format("Invalid queue '%1' (%2): Realms can only be confined to serial queues or the main queue.",
                                    dispatch_queue_get_label(queue) ?: "<nil>", class_getName(cls));
            throw std::logic_error(msg);
        }
    }
    dispatch_retain(m_queue);
    if (dispatch_queue_get_specific(m_queue, c_queue_key) == nullptr) {
        dispatch_queue_set_specific(m_queue, c_queue_key, queue, nullptr);
    }
}

DispatchQueueScheduler::~DispatchQueueScheduler()
{
    dispatch_release(m_queue);
    if (m_callback)
        Block_release(m_callback);
}

void DispatchQueueScheduler::notify()
{
    dispatch_async(m_queue, m_callback);
}

void DispatchQueueScheduler::set_notify_callback(std::function<void ()> callback)
{
    m_callback = Block_copy(^{
        callback();
    });
}

bool DispatchQueueScheduler::is_on_thread() const noexcept
{
    return dispatch_get_specific(c_queue_key) == m_queue;
}

bool DispatchQueueScheduler::is_same_as(const Scheduler* other) const noexcept
{
    auto o = dynamic_cast<const DispatchQueueScheduler*>(other);
    return (o && (o->m_queue == m_queue));
}

} // anonymous namespace

namespace realm {
namespace util {
std::shared_ptr<Scheduler> Scheduler::make_default()
{
    return std::make_shared<RunLoopScheduler>();
}

std::shared_ptr<Scheduler> Scheduler::make_runloop(CFRunLoopRef run_loop)
{
    return std::make_shared<RunLoopScheduler>(run_loop ?: CFRunLoopGetCurrent());
}

std::shared_ptr<Scheduler> Scheduler::make_dispatch(void* queue)
{
    return std::make_shared<DispatchQueueScheduler>(static_cast<dispatch_queue_t>(queue));
}
} // namespace util
} // namespace realm
<|endoftext|>


<commit_before><commit_msg>Pipe cmd-z back to browser.<commit_after><|endoftext|>


<commit_before>#include "../bitmap.h"
#include "../trans-bitmap.h"
#include "scroll-list.h"
#include "../font.h"
#include <math.h>
#include "../debug.h"

namespace Gui{

static const double FONT_SPACER = 1.3;
static const int GradientMax = 50;

double SCROLL_STEP = 20;
double SCROLL_MOTION = 2;
const int SCROLL_WAIT = 4;

static int selectedGradientStart(){
    static int color = Graphics::makeColor(19, 167, 168);
    return color;
}

static int selectedGradientEnd(){
    static int color = Graphics::makeColor(27, 237, 239);
    return color;
}

ScrollItem::ScrollItem(){
}

ScrollItem::~ScrollItem(){
}

ScrollList::ScrollList():
currentIndex(0),
fontSpacingX(0),
fontSpacingY(0),
currentPosition(0),
scrollWait(0),
selectedGradient(GradientMax, selectedGradientStart(), selectedGradientEnd()),
useGradient(false),
useHighlight(false),
allowWrap(true),
scroll(0){}

ScrollList::ScrollList(const ScrollList & copy):
currentIndex(copy.currentIndex),
fontSpacingX(copy.fontSpacingX),
fontSpacingY(copy.fontSpacingY),
currentPosition(copy.currentPosition),
scrollWait(copy.scrollWait),
selectedGradient(GradientMax, selectedGradientStart(), selectedGradientEnd()),
useGradient(copy.useGradient),
useHighlight(copy.useHighlight),
allowWrap(true),
scroll(0){}

ScrollList::~ScrollList(){}

ScrollList & ScrollList::operator=(const ScrollList & copy){
    return *this;
}

void ScrollList::act(){
    if (scrollWait == 0){
        if (scroll > SCROLL_MOTION){
            scroll -= SCROLL_MOTION;
        } else if (scroll < -SCROLL_MOTION){
            scroll += SCROLL_MOTION;
        } else {
            scroll = 0;
            currentPosition = currentIndex;
        }
    } else {
        scrollWait -= 1;
    }

    /*
    if (scrollWait > 0){
        scrollWait -= 1;
    } else {
        currentPosition = currentIndex;
    }
    */
}

/* this is the smooth scroll stuff from context-box */
void ScrollList::doDraw(int x, int y, int middle_x, int min_y, int max_y, const Font & font, int current, int selected, const Graphics::Bitmap & area, int direction){
    int fadeAlpha = 255;
    while (y < max_y && y > min_y){
        int pick = current;
        while (pick < 0){
            pick += text.size();
        }
        pick = pick % text.size();

        Util::ReferenceCount<ScrollItem> option = text[pick];
        const int startx = middle_x - option->size(font) / 2;

        /* draw current selection, make it glow */
        if (current == selected){
            Graphics::Bitmap::transBlender(0, 0, 0, fadeAlpha);
            Graphics::TranslucentBitmap translucent(area);
            const int color = useGradient ? selectedGradient.current() : selectedGradientStart();
            option->draw(x + startx, y, color, translucent, font);
#if 0
            if (option->isAdjustable()){
                const int triangleSize = 14;
                int cx = startx - 15;
                int cy = (int)(y + (font.getHeight()/FONT_SPACER) / 2 + 2);

                /* do the triangles need to be translucent? */
                translucent.equilateralTriangle(cx, cy, 180, triangleSize, option->getLeftColor());

                cx = (x + startx + font.textLength(option->getName().c_str()))+15;
                translucent.equilateralTriangle(cx, cy, 0, triangleSize, option->getRightColor());
            }
#endif
        } else {
            /* draw some other item, and fade it */
            int count = (int) fabs((double) current - (double) selected);
            /* TODO: maybe scale by the number of total items instead of using 35 */
            int textAlpha = fadeAlpha - (count * 35);
            if (textAlpha < 0){
                textAlpha = 0;
            }
            Graphics::Bitmap::transBlender(0, 0, 0, textAlpha);
            const int color = Graphics::makeColor(255,255,255);
            option->draw(x + startx, y, color, area.translucent(), font);
            // font.printf(x + startx, y, color, area.translucent(), option->getName(), 0);
        }

        if (text.size() == 1){
            return;
        }

        current += direction;
        y += direction * font.getHeight() / FONT_SPACER;
    }
}

void ScrollList::render(const Graphics::Bitmap & where, const Font & font){

    SCROLL_STEP = font.getHeight() / 2;
    SCROLL_MOTION = SCROLL_STEP / 8;

    // Global::debug(0) << "Scroll is " << scroll << std::endl;
    int y = where.getHeight() / 2 + scroll - font.getHeight() / 2;
    int min_y = 0 - font.getHeight() / FONT_SPACER;
    int max_y = where.getHeight();

    doDraw(0, y, where.getWidth() / 2, min_y, max_y, font, currentIndex, currentIndex, where, 1);

    /* draw above the current selection */
    doDraw(0, y - font.getHeight() / FONT_SPACER, where.getWidth() / 2, min_y, max_y, font, currentIndex - 1, currentIndex, where, -1);

    /*
    int y = 0;
    int x = 5;
    int current = currentIndex;
    while (y < where.getHeight()){
        Util::ReferenceCount<ScrollItem> & item = this->text[current];
        if (current == currentIndex){
            Graphics::Bitmap::transBlender(0, 0, 0, 255);
        } else {
            Graphics::Bitmap::transBlender(0, 0, 0, 128);
        }
        item->draw(x, y, where.translucent(), font);
        y += font.getHeight();
        current = (current + 1 + this->text.size()) % this->text.size();
    }
    */
}

void ScrollList::addItem(const Util::ReferenceCount<ScrollItem> & text){
    this->text.push_back(text);
}

void ScrollList::addItems(const std::vector<Util::ReferenceCount<ScrollItem> > & texts){
    this->text.insert(text.end(), texts.begin(), texts.end());
}

void ScrollList::setPosition(const Gui::Coordinate & location){
    this->position = location;
}

bool ScrollList::next(){
    currentIndex++;
    scroll = SCROLL_STEP;
    if (scrollWait == 0){
        scrollWait = SCROLL_WAIT;
    }
    if (currentIndex >= text.size()){
	if (allowWrap){
	    currentIndex = 0;
	    return true;
	} else {
	    currentIndex = text.size()-1;
	    return false;
	}
    }
    return true;
}

bool ScrollList::previous(){
    scroll = -SCROLL_STEP;
    if (scrollWait == 0){
        scrollWait = SCROLL_WAIT;
    }
    if (currentIndex > 0){
	currentIndex--;
    } else if (currentIndex == 0){
	if (allowWrap){
	    currentIndex = text.size()-1;
	    return true;
	} else {
	    return false;
	}
    }
    return true;
}

bool ScrollList::setCurrentIndex(unsigned int index){
    if (index >= text.size()){
	return false;
    }
    currentIndex = index;
    return true;
}

}
<commit_msg>[gui] smoother scrolling<commit_after>#include "../bitmap.h"
#include "../trans-bitmap.h"
#include "scroll-list.h"
#include "../font.h"
#include <math.h>
#include "../debug.h"

namespace Gui{

static const double FONT_SPACER = 1.3;
static const int GradientMax = 50;

double SCROLL_STEP = 20;
const double SCROLL_MOTION = 1.2;
const int SCROLL_WAIT = 4;

static int selectedGradientStart(){
    static int color = Graphics::makeColor(19, 167, 168);
    return color;
}

static int selectedGradientEnd(){
    static int color = Graphics::makeColor(27, 237, 239);
    return color;
}

ScrollItem::ScrollItem(){
}

ScrollItem::~ScrollItem(){
}

ScrollList::ScrollList():
currentIndex(0),
fontSpacingX(0),
fontSpacingY(0),
currentPosition(0),
scrollWait(0),
selectedGradient(GradientMax, selectedGradientStart(), selectedGradientEnd()),
useGradient(false),
useHighlight(false),
allowWrap(true),
scroll(0){}

ScrollList::ScrollList(const ScrollList & copy):
currentIndex(copy.currentIndex),
fontSpacingX(copy.fontSpacingX),
fontSpacingY(copy.fontSpacingY),
currentPosition(copy.currentPosition),
scrollWait(copy.scrollWait),
selectedGradient(GradientMax, selectedGradientStart(), selectedGradientEnd()),
useGradient(copy.useGradient),
useHighlight(copy.useHighlight),
allowWrap(true),
scroll(0){}

ScrollList::~ScrollList(){}

ScrollList & ScrollList::operator=(const ScrollList & copy){
    return *this;
}

void ScrollList::act(){
    if (scrollWait == 0){
        if (scroll > SCROLL_MOTION){
            // scroll -= SCROLL_MOTION;
            scroll /= SCROLL_MOTION;
        } else if (scroll < -SCROLL_MOTION){
            // scroll += SCROLL_MOTION;
            scroll /= SCROLL_MOTION;
        } else {
            scroll = 0;
            currentPosition = currentIndex;
        }
    } else {
        scrollWait -= 1;
    }

    /*
    if (scrollWait > 0){
        scrollWait -= 1;
    } else {
        currentPosition = currentIndex;
    }
    */
}

/* this is the smooth scroll stuff from context-box */
void ScrollList::doDraw(int x, int y, int middle_x, int min_y, int max_y, const Font & font, int current, int selected, const Graphics::Bitmap & area, int direction){
    int fadeAlpha = 255;
    while (y < max_y && y > min_y){
        int pick = current;
        while (pick < 0){
            pick += text.size();
        }
        pick = pick % text.size();

        Util::ReferenceCount<ScrollItem> option = text[pick];
        const int startx = middle_x - option->size(font) / 2;

        /* draw current selection, make it glow */
        if (current == selected){
            Graphics::Bitmap::transBlender(0, 0, 0, fadeAlpha);
            Graphics::TranslucentBitmap translucent(area);
            const int color = useGradient ? selectedGradient.current() : selectedGradientStart();
            option->draw(x + startx, y, color, translucent, font);
#if 0
            if (option->isAdjustable()){
                const int triangleSize = 14;
                int cx = startx - 15;
                int cy = (int)(y + (font.getHeight()/FONT_SPACER) / 2 + 2);

                /* do the triangles need to be translucent? */
                translucent.equilateralTriangle(cx, cy, 180, triangleSize, option->getLeftColor());

                cx = (x + startx + font.textLength(option->getName().c_str()))+15;
                translucent.equilateralTriangle(cx, cy, 0, triangleSize, option->getRightColor());
            }
#endif
        } else {
            /* draw some other item, and fade it */
            int count = (int) fabs((double) current - (double) selected);
            /* TODO: maybe scale by the number of total items instead of using 35 */
            int textAlpha = fadeAlpha - (count * 35);
            if (textAlpha < 0){
                textAlpha = 0;
            }
            Graphics::Bitmap::transBlender(0, 0, 0, textAlpha);
            const int color = Graphics::makeColor(255,255,255);
            option->draw(x + startx, y, color, area.translucent(), font);
            // font.printf(x + startx, y, color, area.translucent(), option->getName(), 0);
        }

        if (text.size() == 1){
            return;
        }

        current += direction;
        y += direction * font.getHeight() / FONT_SPACER;
    }
}

void ScrollList::render(const Graphics::Bitmap & where, const Font & font){

    SCROLL_STEP = font.getHeight() / 2;
    // SCROLL_MOTION = SCROLL_STEP / 8;

    // Global::debug(0) << "Scroll is " << scroll << std::endl;
    int y = where.getHeight() / 2 + scroll - font.getHeight() / 2;
    int min_y = 0 - font.getHeight() / FONT_SPACER;
    int max_y = where.getHeight();

    doDraw(0, y, where.getWidth() / 2, min_y, max_y, font, currentIndex, currentIndex, where, 1);

    /* draw above the current selection */
    doDraw(0, y - font.getHeight() / FONT_SPACER, where.getWidth() / 2, min_y, max_y, font, currentIndex - 1, currentIndex, where, -1);

    /*
    int y = 0;
    int x = 5;
    int current = currentIndex;
    while (y < where.getHeight()){
        Util::ReferenceCount<ScrollItem> & item = this->text[current];
        if (current == currentIndex){
            Graphics::Bitmap::transBlender(0, 0, 0, 255);
        } else {
            Graphics::Bitmap::transBlender(0, 0, 0, 128);
        }
        item->draw(x, y, where.translucent(), font);
        y += font.getHeight();
        current = (current + 1 + this->text.size()) % this->text.size();
    }
    */
}

void ScrollList::addItem(const Util::ReferenceCount<ScrollItem> & text){
    this->text.push_back(text);
}

void ScrollList::addItems(const std::vector<Util::ReferenceCount<ScrollItem> > & texts){
    this->text.insert(text.end(), texts.begin(), texts.end());
}

void ScrollList::setPosition(const Gui::Coordinate & location){
    this->position = location;
}

bool ScrollList::next(){
    currentIndex++;
    scroll = SCROLL_STEP;
    if (scrollWait == 0){
        scrollWait = SCROLL_WAIT;
    }
    if (currentIndex >= text.size()){
	if (allowWrap){
	    currentIndex = 0;
	    return true;
	} else {
	    currentIndex = text.size()-1;
	    return false;
	}
    }
    return true;
}

bool ScrollList::previous(){
    scroll = -SCROLL_STEP;
    if (scrollWait == 0){
        scrollWait = SCROLL_WAIT;
    }
    if (currentIndex > 0){
	currentIndex--;
    } else if (currentIndex == 0){
	if (allowWrap){
	    currentIndex = text.size()-1;
	    return true;
	} else {
	    return false;
	}
    }
    return true;
}

bool ScrollList::setCurrentIndex(unsigned int index){
    if (index >= text.size()){
	return false;
    }
    currentIndex = index;
    return true;
}

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "LocalizedStrings.h"

#include "IntSize.h"
#include "NotImplemented.h"
#include "PlatformString.h"
#include "StringBuilder.h"

#include "WebKit.h"
#include "WebKitClient.h"
#include "WebLocalizedString.h"
#include "WebString.h"

using WebKit::WebLocalizedString;

namespace WebCore {

static String query(WebLocalizedString::Name name)
{
    return WebKit::webKitClient()->queryLocalizedString(name);
}

static String query(WebLocalizedString::Name name, int numericValue)
{
    return WebKit::webKitClient()->queryLocalizedString(name, numericValue);
}

String searchableIndexIntroduction()
{
    return query(WebLocalizedString::SearchableIndexIntroduction);
}

String submitButtonDefaultLabel()
{
    return query(WebLocalizedString::SubmitButtonDefaultLabel);
}

String inputElementAltText()
{
    return query(WebLocalizedString::InputElementAltText);
}

String resetButtonDefaultLabel()
{
    return query(WebLocalizedString::ResetButtonDefaultLabel);
}

String fileButtonChooseFileLabel()
{
    return query(WebLocalizedString::FileButtonChooseFileLabel);
}

String fileButtonNoFileSelectedLabel()
{
    return query(WebLocalizedString::FileButtonNoFileSelectedLabel);
}

String searchMenuNoRecentSearchesText()
{
    return query(WebLocalizedString::SearchMenuNoRecentSearchesText);
}
String searchMenuRecentSearchesText()
{
    return query(WebLocalizedString::SearchMenuRecentSearchesText);
}
String searchMenuClearRecentSearchesText()
{
    return query(WebLocalizedString::SearchMenuClearRecentSearchesText);
}

String AXWebAreaText()
{
    return query(WebLocalizedString::AXWebAreaText);
}

String AXLinkText()
{
    return query(WebLocalizedString::AXLinkText);
}

String AXListMarkerText()
{
    return query(WebLocalizedString::AXListMarkerText);
}

String AXImageMapText()
{
    return query(WebLocalizedString::AXImageMapText);
}

String AXHeadingText()
{
    return query(WebLocalizedString::AXHeadingText);
}

String AXDefinitionListTermText()
{
    notImplemented();
    return String("term");
}

String AXDefinitionListDefinitionText()
{
    notImplemented();
    return String("definition");
}

String AXButtonActionVerb()
{
    return query(WebLocalizedString::AXButtonActionVerb);
}

String AXRadioButtonActionVerb()
{
    return query(WebLocalizedString::AXRadioButtonActionVerb);
}

String AXTextFieldActionVerb()
{
    return query(WebLocalizedString::AXTextFieldActionVerb);
}

String AXCheckedCheckBoxActionVerb()
{
    return query(WebLocalizedString::AXCheckedCheckBoxActionVerb);
}

String AXUncheckedCheckBoxActionVerb()
{
    return query(WebLocalizedString::AXUncheckedCheckBoxActionVerb);
}

String AXLinkActionVerb()
{
    return query(WebLocalizedString::AXLinkActionVerb);
}

String multipleFileUploadText(unsigned numberOfFiles)
{
    return query(WebLocalizedString::MultipleFileUploadText, numberOfFiles);
}

// Used in FTPDirectoryDocument.cpp
String unknownFileSizeText()
{
    return String();
}

// The following two functions are not declared in LocalizedStrings.h.
// They are used by the menu for the HTML keygen tag.
String keygenMenuHighGradeKeySize()
{
    return query(WebLocalizedString::KeygenMenuHighGradeKeySize);
}

String keygenMenuMediumGradeKeySize()
{
    return query(WebLocalizedString::KeygenMenuMediumGradeKeySize);
}

// Used in ImageDocument.cpp as the title for pages when that page is an image.
String imageTitle(const String& filename, const IntSize& size)
{
    // Note that we cannot use String::format because it works for ASCII only.
    StringBuilder result;
    result.append(filename);
    result.append(" (");
    result.append(String::number(size.width()));
    result.append(static_cast<UChar>(0xD7));  // U+00D7 (multiplication sign)
    result.append(String::number(size.height()));
    result.append(")");
    return result.toString();
}

// We don't use these strings, so they return an empty String. We can't just
// make them asserts because webcore still calls them.
String contextMenuItemTagOpenLinkInNewWindow() { return String(); }
String contextMenuItemTagDownloadLinkToDisk() { return String(); }
String contextMenuItemTagCopyLinkToClipboard() { return String(); }
String contextMenuItemTagOpenImageInNewWindow() { return String(); }
String contextMenuItemTagDownloadImageToDisk() { return String(); }
String contextMenuItemTagCopyImageToClipboard() { return String(); }
String contextMenuItemTagOpenFrameInNewWindow() { return String(); }
String contextMenuItemTagCopy() { return String(); }
String contextMenuItemTagGoBack() { return String(); }
String contextMenuItemTagGoForward() { return String(); }
String contextMenuItemTagStop() { return String(); }
String contextMenuItemTagReload() { return String(); }
String contextMenuItemTagCut() { return String(); }
String contextMenuItemTagPaste() { return String(); }
String contextMenuItemTagNoGuessesFound() { return String(); }
String contextMenuItemTagIgnoreSpelling() { return String(); }
String contextMenuItemTagLearnSpelling() { return String(); }
String contextMenuItemTagSearchWeb() { return String(); }
String contextMenuItemTagLookUpInDictionary() { return String(); }
String contextMenuItemTagOpenLink() { return String(); }
String contextMenuItemTagIgnoreGrammar() { return String(); }
String contextMenuItemTagSpellingMenu() { return String(); }
String contextMenuItemTagCheckSpelling() { return String(); }
String contextMenuItemTagCheckSpellingWhileTyping() { return String(); }
String contextMenuItemTagCheckGrammarWithSpelling() { return String(); }
String contextMenuItemTagFontMenu() { return String(); }
String contextMenuItemTagBold() { return String(); }
String contextMenuItemTagItalic() { return String(); }
String contextMenuItemTagUnderline() { return String(); }
String contextMenuItemTagOutline() { return String(); }
String contextMenuItemTagWritingDirectionMenu() { return String(); }
String contextMenuItemTagTextDirectionMenu() { return String(); }
String contextMenuItemTagDefaultDirection() { return String(); }
String contextMenuItemTagLeftToRight() { return String(); }
String contextMenuItemTagRightToLeft() { return String(); }
String contextMenuItemTagInspectElement() { return String(); }
String contextMenuItemTagShowSpellingPanel(bool show) { return String(); }
String mediaElementLiveBroadcastStateText() { return String(); }
String mediaElementLoadingStateText() { return String(); }

} // namespace WebCore
<commit_msg>Add dummy implementation for newly added localized string in WebKit. This is to fix compiling errors in incoming WebKit merge.<commit_after>/*
 * Copyright (C) 2009 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "LocalizedStrings.h"

#include "IntSize.h"
#include "NotImplemented.h"
#include "PlatformString.h"
#include "StringBuilder.h"

#include "WebKit.h"
#include "WebKitClient.h"
#include "WebLocalizedString.h"
#include "WebString.h"

using WebKit::WebLocalizedString;

namespace WebCore {

static String query(WebLocalizedString::Name name)
{
    return WebKit::webKitClient()->queryLocalizedString(name);
}

static String query(WebLocalizedString::Name name, int numericValue)
{
    return WebKit::webKitClient()->queryLocalizedString(name, numericValue);
}

String searchableIndexIntroduction()
{
    return query(WebLocalizedString::SearchableIndexIntroduction);
}

String submitButtonDefaultLabel()
{
    return query(WebLocalizedString::SubmitButtonDefaultLabel);
}

String inputElementAltText()
{
    return query(WebLocalizedString::InputElementAltText);
}

String resetButtonDefaultLabel()
{
    return query(WebLocalizedString::ResetButtonDefaultLabel);
}

String fileButtonChooseFileLabel()
{
    return query(WebLocalizedString::FileButtonChooseFileLabel);
}

String fileButtonNoFileSelectedLabel()
{
    return query(WebLocalizedString::FileButtonNoFileSelectedLabel);
}

String searchMenuNoRecentSearchesText()
{
    return query(WebLocalizedString::SearchMenuNoRecentSearchesText);
}
String searchMenuRecentSearchesText()
{
    return query(WebLocalizedString::SearchMenuRecentSearchesText);
}
String searchMenuClearRecentSearchesText()
{
    return query(WebLocalizedString::SearchMenuClearRecentSearchesText);
}

String AXWebAreaText()
{
    return query(WebLocalizedString::AXWebAreaText);
}

String AXLinkText()
{
    return query(WebLocalizedString::AXLinkText);
}

String AXListMarkerText()
{
    return query(WebLocalizedString::AXListMarkerText);
}

String AXImageMapText()
{
    return query(WebLocalizedString::AXImageMapText);
}

String AXHeadingText()
{
    return query(WebLocalizedString::AXHeadingText);
}

String AXDefinitionListTermText()
{
    notImplemented();
    return String("term");
}

String AXDefinitionListDefinitionText()
{
    notImplemented();
    return String("definition");
}

String AXButtonActionVerb()
{
    return query(WebLocalizedString::AXButtonActionVerb);
}

String AXRadioButtonActionVerb()
{
    return query(WebLocalizedString::AXRadioButtonActionVerb);
}

String AXTextFieldActionVerb()
{
    return query(WebLocalizedString::AXTextFieldActionVerb);
}

String AXCheckedCheckBoxActionVerb()
{
    return query(WebLocalizedString::AXCheckedCheckBoxActionVerb);
}

String AXUncheckedCheckBoxActionVerb()
{
    return query(WebLocalizedString::AXUncheckedCheckBoxActionVerb);
}

String AXLinkActionVerb()
{
    return query(WebLocalizedString::AXLinkActionVerb);
}

String multipleFileUploadText(unsigned numberOfFiles)
{
    return query(WebLocalizedString::MultipleFileUploadText, numberOfFiles);
}

// Used in FTPDirectoryDocument.cpp
String unknownFileSizeText()
{
    return String();
}

// The following two functions are not declared in LocalizedStrings.h.
// They are used by the menu for the HTML keygen tag.
String keygenMenuHighGradeKeySize()
{
    return query(WebLocalizedString::KeygenMenuHighGradeKeySize);
}

String keygenMenuMediumGradeKeySize()
{
    return query(WebLocalizedString::KeygenMenuMediumGradeKeySize);
}

// Used in ImageDocument.cpp as the title for pages when that page is an image.
String imageTitle(const String& filename, const IntSize& size)
{
    // Note that we cannot use String::format because it works for ASCII only.
    StringBuilder result;
    result.append(filename);
    result.append(" (");
    result.append(String::number(size.width()));
    result.append(static_cast<UChar>(0xD7));  // U+00D7 (multiplication sign)
    result.append(String::number(size.height()));
    result.append(")");
    return result.toString();
}

// We don't use these strings, so they return an empty String. We can't just
// make them asserts because webcore still calls them.
String contextMenuItemTagOpenLinkInNewWindow() { return String(); }
String contextMenuItemTagDownloadLinkToDisk() { return String(); }
String contextMenuItemTagCopyLinkToClipboard() { return String(); }
String contextMenuItemTagOpenImageInNewWindow() { return String(); }
String contextMenuItemTagDownloadImageToDisk() { return String(); }
String contextMenuItemTagCopyImageToClipboard() { return String(); }
String contextMenuItemTagOpenFrameInNewWindow() { return String(); }
String contextMenuItemTagCopy() { return String(); }
String contextMenuItemTagGoBack() { return String(); }
String contextMenuItemTagGoForward() { return String(); }
String contextMenuItemTagStop() { return String(); }
String contextMenuItemTagReload() { return String(); }
String contextMenuItemTagCut() { return String(); }
String contextMenuItemTagPaste() { return String(); }
String contextMenuItemTagNoGuessesFound() { return String(); }
String contextMenuItemTagIgnoreSpelling() { return String(); }
String contextMenuItemTagLearnSpelling() { return String(); }
String contextMenuItemTagSearchWeb() { return String(); }
String contextMenuItemTagLookUpInDictionary() { return String(); }
String contextMenuItemTagOpenLink() { return String(); }
String contextMenuItemTagIgnoreGrammar() { return String(); }
String contextMenuItemTagSpellingMenu() { return String(); }
String contextMenuItemTagCheckSpelling() { return String(); }
String contextMenuItemTagCheckSpellingWhileTyping() { return String(); }
String contextMenuItemTagCheckGrammarWithSpelling() { return String(); }
String contextMenuItemTagFontMenu() { return String(); }
String contextMenuItemTagBold() { return String(); }
String contextMenuItemTagItalic() { return String(); }
String contextMenuItemTagUnderline() { return String(); }
String contextMenuItemTagOutline() { return String(); }
String contextMenuItemTagWritingDirectionMenu() { return String(); }
String contextMenuItemTagTextDirectionMenu() { return String(); }
String contextMenuItemTagDefaultDirection() { return String(); }
String contextMenuItemTagLeftToRight() { return String(); }
String contextMenuItemTagRightToLeft() { return String(); }
String contextMenuItemTagInspectElement() { return String(); }
String contextMenuItemTagShowSpellingPanel(bool show) { return String(); }
String mediaElementLiveBroadcastStateText() { return String(); }
String mediaElementLoadingStateText() { return String(); }

String localizedMediaControlElementString(const String& /*name*/)
{
    // FIXME: to be fixed.
    return String();
}

String localizedMediaControlElementHelpText(const String& /*name*/)
{
    // FIXME: to be fixed.
    return String();
}

String localizedMediaTimeDescription(float /*time*/)
{
    // FIXME: to be fixed.
    return String();
}

} // namespace WebCore
<|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.

#include "config.h"

#include "testing/gtest/include/gtest/gtest.h"

#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "webkit/tools/test_shell/test_shell_test.h"

namespace {

class BookmarkletTest : public TestShellTest {
 public:
  virtual void SetUp() {
    TestShellTest::SetUp();

    test_shell_->LoadURL(L"data:text/html,start page");
    test_shell_->WaitTestFinished();
  }
};

}

TEST_F(BookmarkletTest, Redirect) {
  test_shell_->LoadURL(L"javascript:location.href='data:text/plain,SUCCESS'");
  test_shell_->WaitTestFinished();
  std::wstring text = test_shell_->GetDocumentText();
  EXPECT_EQ(L"SUCCESS", text);
}

TEST_F(BookmarkletTest, NonEmptyResult) {
  test_shell_->LoadURL(L"javascript:false");
  MessageLoop::current()->RunAllPending();
  std::wstring text = test_shell_->GetDocumentText();
  EXPECT_EQ(L"false", text);

  test_shell_->LoadURL(L"javascript:'hello world'");
  MessageLoop::current()->RunAllPending();
  text = test_shell_->GetDocumentText();
  EXPECT_EQ(L"hello world", text);
}

TEST_F(BookmarkletTest, DocumentWrite) {
  test_shell_->LoadURL(
      L"javascript:document.open();"
      L"document.write('hello world');"
      L"document.close()");
  MessageLoop::current()->RunAllPending();
  std::wstring text = test_shell_->GetDocumentText();
  EXPECT_EQ(L"hello world", text);
}

<commit_msg>Disable this bookmarklet test since it covers functionality that JSC doesn't support.<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.

#include "config.h"

#include "testing/gtest/include/gtest/gtest.h"

#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "webkit/tools/test_shell/test_shell_test.h"

namespace {

class BookmarkletTest : public TestShellTest {
 public:
  virtual void SetUp() {
    TestShellTest::SetUp();

    test_shell_->LoadURL(L"data:text/html,start page");
    test_shell_->WaitTestFinished();
  }
};

}

TEST_F(BookmarkletTest, Redirect) {
  test_shell_->LoadURL(L"javascript:location.href='data:text/plain,SUCCESS'");
  test_shell_->WaitTestFinished();
  std::wstring text = test_shell_->GetDocumentText();
  EXPECT_EQ(L"SUCCESS", text);
}

TEST_F(BookmarkletTest, NonEmptyResult) {
  std::wstring text;

  // TODO(darin): This test fails in a JSC build.  WebCore+JSC does not really
  // need to support this usage until WebCore supports javascript: URLs that
  // generate content (https://bugs.webkit.org/show_bug.cgi?id=14959).  It is
  // important to note that Safari does not support bookmarklets, and this is
  // really an edge case.  Our behavior with V8 is consistent with FF and IE.
#if 0
  test_shell_->LoadURL(L"javascript:false");
  MessageLoop::current()->RunAllPending();
  text = test_shell_->GetDocumentText();
  EXPECT_EQ(L"false", text);
#endif

  test_shell_->LoadURL(L"javascript:'hello world'");
  MessageLoop::current()->RunAllPending();
  text = test_shell_->GetDocumentText();
  EXPECT_EQ(L"hello world", text);
}

TEST_F(BookmarkletTest, DocumentWrite) {
  test_shell_->LoadURL(
      L"javascript:document.open();"
      L"document.write('hello world');"
      L"document.close()");
  MessageLoop::current()->RunAllPending();
  std::wstring text = test_shell_->GetDocumentText();
  EXPECT_EQ(L"hello world", text);
}

<|endoftext|>


<commit_before>/*
 * Copyright 2014 Jules Cléro
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

extern "C"
{
/** LibLineFollowing */
#include "SystemDrone.h"
}

#include <libDroneMovement/Drone.hpp>
#include <libDroneVideo/FrameGrabber.hpp>
#include <libDroneVideo/LineDetector.hpp>
#include <chrono>
#include <thread>

int main(void)
{
    using namespace ghost::libDroneMovement;
    using namespace ghost::libDroneVideo;

    Drone drone("127.0.0.1");
    FrameGrabber frameGrabber = FrameGrabber();

    // Auto Pilot initialization
    inC_SystemDrone inputAutoPilot;
    outC_SystemDrone outputAutoPilot;

    SystemDrone_init(&outputAutoPilot);

    drone.takeOff();

    // Drop Frames
    frameGrabber.getNextVerticalFrame(),
    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    double lineAngle = 0.0;
    bool needImageUpdate = true;

    for (size_t i = 0; i < 1000; i++) {

        if (i % 10 == 0) {
            needImageUpdate = true;
        }

        if (needImageUpdate) {
            // Frame Analysis
            std::cout << "Update Image: " << std::endl;
            LineDetector<FrameGrabber::DroneVerticalFrame> lineDetector(
                frameGrabber.getNextVerticalFrame(),
                320,
                240);

            lineAngle = lineDetector.getLineAngle();

            if (std::isnan(lineAngle)) {
                // we don't see the line, let's assume we are good for now
                lineAngle = 0.0;
            }

            inputAutoPilot.LineAngle = lineAngle;
            inputAutoPilot.RightWay = true;
            inputAutoPilot.GoLeft = false;
            inputAutoPilot.GoRight = false;

            inputAutoPilot.ImageUpdate = needImageUpdate;

            needImageUpdate = false;
        } else {
            inputAutoPilot.ImageUpdate = needImageUpdate;
        }

        float currentAngle = drone.getPsiAngle() / 1000.0;

        inputAutoPilot.currentAngle = currentAngle;

        std::cout << "Angle Ligne: " << lineAngle << std::endl;
        std::cout << "Angle Drone: " << currentAngle << std::endl;

        std::cout << "Yaw speed Drone: " << outputAutoPilot.Yaw << std::endl;

        SystemDrone(&inputAutoPilot, &outputAutoPilot);

        drone.movement(
            outputAutoPilot.Roll,
            outputAutoPilot.Pitch,
            outputAutoPilot.Gaz,
            outputAutoPilot.Yaw);

        std::this_thread::sleep_for(std::chrono::milliseconds(30));
    }

    drone.land();

    return 0;
}
<commit_msg>[Core] Add support of the emergency mode of LibLineFollowing library<commit_after>/*
 * Copyright 2014 Jules Cléro
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

extern "C"
{
/** LibLineFollowing */
#include "SystemDrone.h"
}

#include <libDroneMovement/Drone.hpp>
#include <libDroneVideo/FrameGrabber.hpp>
#include <libDroneVideo/LineDetector.hpp>
#include <chrono>
#include <thread>

int main(void)
{
    using namespace ghost::libDroneMovement;
    using namespace ghost::libDroneVideo;

    Drone drone("127.0.0.1");
    FrameGrabber frameGrabber = FrameGrabber();

    // Auto Pilot initialization
    inC_SystemDrone inputAutoPilot;
    outC_SystemDrone outputAutoPilot;

    SystemDrone_init(&outputAutoPilot);

    drone.takeOff();

    // Drop Frames
    frameGrabber.getNextVerticalFrame(),
    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    double lineAngle = 0.0;
    bool needToGoLeft = false;
    bool needToGoRight = false;
    bool needImageUpdate = true;
    bool lineDetected = true;
    int count = 0;

    for (size_t i = 0; i < 1000; i++) {

        if (i % 10 == 0) {
            needImageUpdate = true;
        }

        if (needImageUpdate) {
            // Frame Analysis
            std::cout << "Update Image: " << std::endl;
            LineDetector<FrameGrabber::DroneVerticalFrame> lineDetector(
                frameGrabber.getNextVerticalFrame(),
                320,
                240);

            lineDetected = true;
            lineAngle = lineDetector.getLineAngle();
            needToGoRight = lineDetector.needToGoRight();
            needToGoLeft = lineDetector.needToGoLeft();

            if (std::isnan(lineAngle)) {
                // we don't see the line, let's assume we are good for now
                lineAngle = 0.0;
                count++;
            } else {
                lineDetected = true;
                count = 0;
            }

            if (count >= 5) {
                lineDetected = false;
            }

            inputAutoPilot.LineAngle = lineAngle;
            inputAutoPilot.RightWay = true;
            inputAutoPilot.GoLeft = needToGoLeft;
            inputAutoPilot.GoRight = needToGoRight;

            inputAutoPilot.ImageUpdate = needImageUpdate;
            inputAutoPilot.LineDetected = lineDetected;

            needImageUpdate = false;
        } else {
            inputAutoPilot.ImageUpdate = needImageUpdate;
        }

        float currentAngle = drone.getPsiAngle() / 1000.0;

        inputAutoPilot.currentAngle = currentAngle;

        std::cout << "Angle Ligne: " << lineAngle << std::endl;
        std::cout << "Angle Drone: " << currentAngle << std::endl;

        std::cout << "Yaw speed Drone: " << outputAutoPilot.Yaw << std::endl;

        SystemDrone(&inputAutoPilot, &outputAutoPilot);

        drone.movement(
            outputAutoPilot.Roll,
            outputAutoPilot.Pitch,
            outputAutoPilot.Gaz,
            outputAutoPilot.Yaw);

        std::this_thread::sleep_for(std::chrono::milliseconds(30));
    }

    drone.land();

    return 0;
}
<|endoftext|>


<commit_before>#include "BufferUtilsPvp.hpp"
#include "utils/conversions.h"

namespace PV {

namespace BufferUtils {

WeightHeader buildWeightHeader(
      bool shared,
      int preLayerNx,
      int preLayerNy,
      int preLayerNf,
      int preLayerNxExt,
      int preLayerNyExt,
      int numArbors,
      double timestamp,
      int nxp,
      int nyp,
      int nfp,
      bool compress,
      float minVal,
      float maxVal) {
   WeightHeader weightHeader;
   ActivityHeader &baseHeader = weightHeader.baseHeader;
   baseHeader.headerSize      = (int)sizeof(weightHeader);
   baseHeader.numParams       = baseHeader.headerSize / 4;
   pvAssert(baseHeader.numParams * 4 == baseHeader.headerSize);

   baseHeader.fileType   = shared ? PVP_KERNEL_FILE_TYPE : PVP_WGT_FILE_TYPE;
   baseHeader.nx         = preLayerNx;
   baseHeader.ny         = preLayerNy;
   baseHeader.nf         = preLayerNf;
   baseHeader.numRecords = numArbors;
   baseHeader.recordSize = 0;

   int numPatches        = preLayerNxExt * preLayerNyExt * preLayerNf;
   int numPatchItems     = nxp * nyp * nfp;
   baseHeader.recordSize = numPatches * weightPatchSize(numPatchItems, compress);
   if (compress) {
      baseHeader.dataSize = (int)sizeof(unsigned char);
      baseHeader.dataType = returnDataType<unsigned char>();
   }
   else {
      baseHeader.dataSize = (int)sizeof(float);
      baseHeader.dataType = returnDataType<float>();
   }
   baseHeader.nxProcs    = 1;
   baseHeader.nyProcs    = 1;
   baseHeader.nxExtended = preLayerNxExt;
   baseHeader.nyExtended = preLayerNyExt;
   baseHeader.kx0        = 0;
   baseHeader.ky0        = 0;
   baseHeader.nBatch     = 1;
   baseHeader.nBands     = numArbors;
   baseHeader.timestamp  = timestamp;

   weightHeader.nxp        = nxp;
   weightHeader.nyp        = nyp;
   weightHeader.nfp        = nfp;
   weightHeader.minVal     = minVal;
   weightHeader.maxVal     = maxVal;
   weightHeader.numPatches = numPatches;

   return weightHeader;
}

WeightHeader buildSharedWeightHeader(
      int nxp,
      int nyp,
      int nfp,
      int numArbors,
      int numPatchesX,
      int numPatchesY,
      int numPatchesF,
      double timestamp,
      bool compress,
      float minVal,
      float maxVal) {
   WeightHeader weightHeader = buildWeightHeader(
         true /* shared weights*/,
         numPatchesX,
         numPatchesY,
         numPatchesF,
         numPatchesX,
         numPatchesY,
         numArbors,
         timestamp,
         nxp,
         nyp,
         nfp,
         compress,
         minVal,
         maxVal);

   return weightHeader;
}

WeightHeader buildNonsharedWeightHeader(
      int nxp,
      int nyp,
      int nfp,
      int numArbors,
      bool extended,
      double timestamp,
      PVLayerLoc const *preLayerLoc,
      PVLayerLoc const *postLayerLoc,
      int numColumnProcesses,
      int numRowProcesses,
      float minVal,
      float maxVal,
      bool compress) {
   int numPatchesX, numPatchesY, numPatchesF, numPatchesXExt, numPatchesYExt;
   calcNumberOfPatches(
         preLayerLoc,
         postLayerLoc,
         numColumnProcesses,
         numRowProcesses,
         extended,
         nxp,
         nyp,
         numPatchesX,
         numPatchesY,
         numPatchesF,
         numPatchesXExt,
         numPatchesYExt);

   WeightHeader weightHeader = buildWeightHeader(
         false /*non-shared weights*/,
         numPatchesX,
         numPatchesY,
         numPatchesF,
         numPatchesXExt,
         numPatchesYExt,
         numArbors,
         timestamp,
         nxp,
         nyp,
         nfp,
         compress,
         minVal,
         maxVal);

   return weightHeader;
}

std::size_t weightPatchSize(int numWeightsInPatch, bool compressed) {
   if (compressed) {
      return weightPatchSize<unsigned char>(numWeightsInPatch);
   }
   else {
      return weightPatchSize<float>(numWeightsInPatch);
   }
}

void calcNumberOfPatches(
      PVLayerLoc const *preLayerLoc,
      PVLayerLoc const *postLayerLoc,
      int numColumnProcesses,
      int numRowProcesses,
      bool extended,
      int nxp,
      int nyp,
      int &numPatchesX,
      int &numPatchesY,
      int &numPatchesF,
      int &numPatchesXExt,
      int &numPatchesYExt) {
   int nxPreRestricted = preLayerLoc->nx * numColumnProcesses;
   int nyPreRestricted = preLayerLoc->ny * numRowProcesses;

   numPatchesX = preLayerLoc->nx * numColumnProcesses;
   numPatchesY = preLayerLoc->ny * numRowProcesses;

   numPatchesXExt = numPatchesX;
   numPatchesYExt = numPatchesY;
   if (extended) {
      int marginX = requiredConvolveMargin(preLayerLoc->nx, postLayerLoc->nx, nxp);
      numPatchesXExt += marginX + marginX;
      int marginY = requiredConvolveMargin(preLayerLoc->ny, postLayerLoc->ny, nyp);
      numPatchesYExt += marginY + marginY;
   }
   numPatchesF = preLayerLoc->nf;
}

} // end namespace BufferUtils
} // end namespace PV
<commit_msg>Fixes recordsize from imperfect merge<commit_after>#include "BufferUtilsPvp.hpp"
#include "utils/conversions.h"

namespace PV {

namespace BufferUtils {

WeightHeader buildWeightHeader(
      bool shared,
      int preLayerNx,
      int preLayerNy,
      int preLayerNf,
      int preLayerNxExt,
      int preLayerNyExt,
      int numArbors,
      double timestamp,
      int nxp,
      int nyp,
      int nfp,
      bool compress,
      float minVal,
      float maxVal) {
   WeightHeader weightHeader;
   ActivityHeader &baseHeader = weightHeader.baseHeader;
   baseHeader.headerSize      = (int)sizeof(weightHeader);
   baseHeader.numParams       = baseHeader.headerSize / 4;
   pvAssert(baseHeader.numParams * 4 == baseHeader.headerSize);

   baseHeader.fileType   = shared ? PVP_KERNEL_FILE_TYPE : PVP_WGT_FILE_TYPE;
   baseHeader.nx         = preLayerNx;
   baseHeader.ny         = preLayerNy;
   baseHeader.nf         = preLayerNf;
   baseHeader.numRecords = numArbors;
   baseHeader.recordSize = 0;

   int numPatches    = preLayerNxExt * preLayerNyExt * preLayerNf;
   int numPatchItems = nxp * nyp * nfp;
   if (compress) {
      baseHeader.dataSize = (int)sizeof(unsigned char);
      baseHeader.dataType = returnDataType<unsigned char>();
   }
   else {
      baseHeader.dataSize = (int)sizeof(float);
      baseHeader.dataType = returnDataType<float>();
   }
   baseHeader.nxProcs    = 1;
   baseHeader.nyProcs    = 1;
   baseHeader.nxExtended = preLayerNxExt;
   baseHeader.nyExtended = preLayerNyExt;
   baseHeader.kx0        = 0;
   baseHeader.ky0        = 0;
   baseHeader.nBatch     = 1;
   baseHeader.nBands     = numArbors;
   baseHeader.timestamp  = timestamp;

   weightHeader.nxp        = nxp;
   weightHeader.nyp        = nyp;
   weightHeader.nfp        = nfp;
   weightHeader.minVal     = minVal;
   weightHeader.maxVal     = maxVal;
   weightHeader.numPatches = numPatches;

   return weightHeader;
}

WeightHeader buildSharedWeightHeader(
      int nxp,
      int nyp,
      int nfp,
      int numArbors,
      int numPatchesX,
      int numPatchesY,
      int numPatchesF,
      double timestamp,
      bool compress,
      float minVal,
      float maxVal) {
   WeightHeader weightHeader = buildWeightHeader(
         true /* shared weights*/,
         numPatchesX,
         numPatchesY,
         numPatchesF,
         numPatchesX,
         numPatchesY,
         numArbors,
         timestamp,
         nxp,
         nyp,
         nfp,
         compress,
         minVal,
         maxVal);

   return weightHeader;
}

WeightHeader buildNonsharedWeightHeader(
      int nxp,
      int nyp,
      int nfp,
      int numArbors,
      bool extended,
      double timestamp,
      PVLayerLoc const *preLayerLoc,
      PVLayerLoc const *postLayerLoc,
      int numColumnProcesses,
      int numRowProcesses,
      float minVal,
      float maxVal,
      bool compress) {
   int numPatchesX, numPatchesY, numPatchesF, numPatchesXExt, numPatchesYExt;
   calcNumberOfPatches(
         preLayerLoc,
         postLayerLoc,
         numColumnProcesses,
         numRowProcesses,
         extended,
         nxp,
         nyp,
         numPatchesX,
         numPatchesY,
         numPatchesF,
         numPatchesXExt,
         numPatchesYExt);

   WeightHeader weightHeader = buildWeightHeader(
         false /*non-shared weights*/,
         numPatchesX,
         numPatchesY,
         numPatchesF,
         numPatchesXExt,
         numPatchesYExt,
         numArbors,
         timestamp,
         nxp,
         nyp,
         nfp,
         compress,
         minVal,
         maxVal);

   return weightHeader;
}

std::size_t weightPatchSize(int numWeightsInPatch, bool compressed) {
   if (compressed) {
      return weightPatchSize<unsigned char>(numWeightsInPatch);
   }
   else {
      return weightPatchSize<float>(numWeightsInPatch);
   }
}

void calcNumberOfPatches(
      PVLayerLoc const *preLayerLoc,
      PVLayerLoc const *postLayerLoc,
      int numColumnProcesses,
      int numRowProcesses,
      bool extended,
      int nxp,
      int nyp,
      int &numPatchesX,
      int &numPatchesY,
      int &numPatchesF,
      int &numPatchesXExt,
      int &numPatchesYExt) {
   int nxPreRestricted = preLayerLoc->nx * numColumnProcesses;
   int nyPreRestricted = preLayerLoc->ny * numRowProcesses;

   numPatchesX = preLayerLoc->nx * numColumnProcesses;
   numPatchesY = preLayerLoc->ny * numRowProcesses;

   numPatchesXExt = numPatchesX;
   numPatchesYExt = numPatchesY;
   if (extended) {
      int marginX = requiredConvolveMargin(preLayerLoc->nx, postLayerLoc->nx, nxp);
      numPatchesXExt += marginX + marginX;
      int marginY = requiredConvolveMargin(preLayerLoc->ny, postLayerLoc->ny, nyp);
      numPatchesYExt += marginY + marginY;
   }
   numPatchesF = preLayerLoc->nf;
}

} // end namespace BufferUtils
} // end namespace PV
<|endoftext|>


<commit_before>#include "../Include/CTransitionVisitor.h"
#include "../Include/CSceneNode.h"
#include "../Include/CSequenceNode.h"
#include "../Include/CKernel.h"

#include "../../Modules/Util/Include/CStats.h"

#include "cocos2d.h"

using namespace cocos2d;

namespace LM
{

CTransitionVisitor::CTransitionVisitor(CKernel* a_pKernel, bool a_bTransitionNext) :
	m_bTransitionNext(a_bTransitionNext),
	m_pKernel(a_pKernel)
{
}

Result CTransitionVisitor::ProcessNodeTopDown(CNode* a_pNode)
{
  // pSequence is the father node of all the scenes.
  // transition will be done on the between the current scene in the node
  // (CSequenceNode::m_iCurrentNode) and the next node
  CSequenceNode* pSequence = dynamic_cast<CSequenceNode*>(a_pNode);
  CSceneNode* pScene = nullptr;
  if (pSequence)
  {
	  pScene = dynamic_cast<CSceneNode*>(pSequence->GetCurrentNode());
  }
  if (pScene)
  {
	  GotoScene(pSequence);
  }
  return RESULT_PRUNE;
}

void CTransitionVisitor::GotoScene(CSequenceNode* a_pSequence)
{
	if (m_pKernel->m_pCurrentScene->m_bDashboardTrigger && m_bTransitionNext)
	{
		// init dashboard
		InitScene(m_pKernel->m_pDashboard);
		return;
	}
	bool bSceneExists = false;
	if (m_bTransitionNext)
	{
		// if TransitioNext offset of 1 scene
		bSceneExists = a_pSequence->OffsetCurrentNode(1);
	}
	else
	{
		// else return to previous scene
		bSceneExists = a_pSequence->OffsetCurrentNode(-1);
	}
	if (!bSceneExists)
	{
		return;
	}
	CSceneNode* pNewSceneNode = dynamic_cast<CSceneNode*>(a_pSequence->GetCurrentNode());
	if (!m_pKernel->PlayerHasScene(pNewSceneNode->GetSceneID()))
	{ // if the player does not have this scene in his list, skip it and go to the next one
		GotoScene(a_pSequence);
		return;
	}
	else if (pNewSceneNode)
	{
		InitScene(pNewSceneNode);
	}

}

void CTransitionVisitor::InitScene(CSceneNode* a_pSceneNode)
{
	Scene* pNewScene = a_pSceneNode->CreateScene();
	a_pSceneNode->init();

	if (m_bTransitionNext)
	{
		Director::getInstance()->replaceScene(TransitionSlideInR::create(0.5f, pNewScene));
	}
	else
	{
		Director::getInstance()->replaceScene(TransitionSlideInL::create(0.5f, pNewScene));
	}
	//CSceneNode* pOldScene = m_pKernel->m_pCurrentScene;
	m_pKernel->m_pCurrentScene = a_pSceneNode;
	//pOldScene->UnInit();

	M_STATS->StartStats();
}

} // namespace LM
<commit_msg>[FIX] dashboard crash on message, but introducing black screens on transition<commit_after>#include "../Include/CTransitionVisitor.h"
#include "../Include/CSceneNode.h"
#include "../Include/CSequenceNode.h"
#include "../Include/CKernel.h"

#include "../../Modules/Util/Include/CStats.h"

#include "cocos2d.h"

using namespace cocos2d;

namespace LM
{

CTransitionVisitor::CTransitionVisitor(CKernel* a_pKernel, bool a_bTransitionNext) :
	m_bTransitionNext(a_bTransitionNext),
	m_pKernel(a_pKernel)
{
}

Result CTransitionVisitor::ProcessNodeTopDown(CNode* a_pNode)
{
  // pSequence is the father node of all the scenes.
  // transition will be done on the between the current scene in the node
  // (CSequenceNode::m_iCurrentNode) and the next node
  CSequenceNode* pSequence = dynamic_cast<CSequenceNode*>(a_pNode);
  CSceneNode* pScene = nullptr;
  if (pSequence)
  {
	  pScene = dynamic_cast<CSceneNode*>(pSequence->GetCurrentNode());
  }
  if (pScene)
  {
	  GotoScene(pSequence);
  }
  return RESULT_PRUNE;
}

void CTransitionVisitor::GotoScene(CSequenceNode* a_pSequence)
{
	if (m_pKernel->m_pCurrentScene->m_bDashboardTrigger && m_bTransitionNext)
	{
		// init dashboard
		InitScene(m_pKernel->m_pDashboard);
		return;
	}
	bool bSceneExists = false;
	if (m_bTransitionNext)
	{
		// if TransitioNext offset of 1 scene
		bSceneExists = a_pSequence->OffsetCurrentNode(1);
	}
	else
	{
		// else return to previous scene
		bSceneExists = a_pSequence->OffsetCurrentNode(-1);
	}
	if (!bSceneExists)
	{
		return;
	}
	CSceneNode* pNewSceneNode = dynamic_cast<CSceneNode*>(a_pSequence->GetCurrentNode());
	if (!m_pKernel->PlayerHasScene(pNewSceneNode->GetSceneID()))
	{ // if the player does not have this scene in his list, skip it and go to the next one
		GotoScene(a_pSequence);
		return;
	}
	else if (pNewSceneNode)
	{
		InitScene(pNewSceneNode);
	}

}

void CTransitionVisitor::InitScene(CSceneNode* a_pSceneNode)
{
	Scene* pNewScene = a_pSceneNode->CreateScene();
	a_pSceneNode->init();

	if (m_bTransitionNext)
	{
		Director::getInstance()->replaceScene(TransitionSlideInR::create(0.5f, pNewScene));
	}
	else
	{
		Director::getInstance()->replaceScene(TransitionSlideInL::create(0.5f, pNewScene));
	}
	CSceneNode* pOldScene = m_pKernel->m_pCurrentScene;
	m_pKernel->m_pCurrentScene = a_pSceneNode;
	pOldScene->UnInit();

	M_STATS->StartStats();
}

} // namespace LM
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkPictureUtils.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDevice.h"
#include "SkPixelRef.h"
#include "SkShader.h"

class PixelRefSet {
public:
    PixelRefSet(SkTDArray<SkPixelRef*>* array) : fArray(array) {}

    // This does a linear search on existing pixelrefs, so if this list gets big
    // we should use a more complex sorted/hashy thing.
    //
    void add(SkPixelRef* pr) {
        uint32_t genID = pr->getGenerationID();
        if (fGenID.find(genID) < 0) {
            *fArray->append() = pr;
            *fGenID.append() = genID;
//            SkDebugf("--- adding [%d] %x %d\n", fArray->count() - 1, pr, genID);
        } else {
//            SkDebugf("--- already have %x %d\n", pr, genID);
        }
    }

private:
    SkTDArray<SkPixelRef*>* fArray;
    SkTDArray<uint32_t>     fGenID;
};

static void not_supported() {
    SkASSERT(!"this method should never be called");
}

static void nothing_to_do() {}

/**
 *  This device will route all bitmaps (primitives and in shaders) to its PRSet.
 *  It should never actually draw anything, so there need not be any pixels
 *  behind its device-bitmap.
 */
class GatherPixelRefDevice : public SkDevice {
private:
    PixelRefSet*  fPRSet;

    void addBitmap(const SkBitmap& bm) {
        fPRSet->add(bm.pixelRef());
    }

    void addBitmapFromPaint(const SkPaint& paint) {
        SkShader* shader = paint.getShader();
        if (shader) {
            SkBitmap bm;
            // Check whether the shader is a gradient in order to short-circuit
            // call to asABitmap to prevent generation of bitmaps from
            // gradient shaders, which implement asABitmap.
            if (SkShader::kNone_GradientType == shader->asAGradient(NULL) &&
                shader->asABitmap(&bm, NULL, NULL)) {
                fPRSet->add(bm.pixelRef());
            }
        }
    }

public:
    GatherPixelRefDevice(const SkBitmap& bm, PixelRefSet* prset) : SkDevice(bm) {
        fPRSet = prset;
    }

    virtual void clear(SkColor color) SK_OVERRIDE {
        nothing_to_do();
    }
    virtual void writePixels(const SkBitmap& bitmap, int x, int y,
                             SkCanvas::Config8888 config8888) SK_OVERRIDE {
        not_supported();
    }

    virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
                            const SkPoint[], const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawRect(const SkDraw&, const SkRect&,
                          const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawOval(const SkDraw&, const SkRect&,
                          const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawPath(const SkDraw&, const SkPath& path,
                          const SkPaint& paint, const SkMatrix* prePathMatrix,
                          bool pathIsMutable) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
                            const SkIRect* srcRectOrNull,
                            const SkMatrix&, const SkPaint&) SK_OVERRIDE {
        this->addBitmap(bitmap);
    }
    virtual void drawBitmapRect(const SkDraw&, const SkBitmap& bitmap,
                                const SkRect* srcOrNull, const SkRect& dst,
                                const SkPaint&) SK_OVERRIDE {
        this->addBitmap(bitmap);
    }
    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
                            int x, int y, const SkPaint& paint) SK_OVERRIDE {
        this->addBitmap(bitmap);
    }
    virtual void drawText(const SkDraw&, const void* text, size_t len,
                          SkScalar x, SkScalar y,
                          const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
                             const SkScalar pos[], SkScalar constY,
                             int, const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
                                const SkPath& path, const SkMatrix* matrix,
                                const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
                              const SkPoint verts[], const SkPoint texs[],
                              const SkColor colors[], SkXfermode* xmode,
                              const uint16_t indices[], int indexCount,
                              const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
                            const SkPaint&) SK_OVERRIDE {
        nothing_to_do();
    }

protected:
    virtual bool onReadPixels(const SkBitmap& bitmap,
                              int x, int y,
                              SkCanvas::Config8888 config8888) SK_OVERRIDE {
        not_supported();
        return false;
    }
};

class NoSaveLayerCanvas : public SkCanvas {
public:
    NoSaveLayerCanvas(SkDevice* device) : INHERITED(device) {}

    // turn saveLayer() into save() for speed, should not affect correctness.
    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
                          SaveFlags flags) SK_OVERRIDE {

        // Like SkPictureRecord, we don't want to create layers, but we do need
        // to respect the save and (possibly) its rect-clip.

        int count = this->INHERITED::save(flags);
        if (bounds) {
            this->INHERITED::clipRectBounds(bounds, flags, NULL);
        }
        return count;
    }

    // disable aa for speed
    virtual bool clipRect(const SkRect& rect, SkRegion::Op op,
                          bool doAA) SK_OVERRIDE {
        return this->INHERITED::clipRect(rect, op, false);
    }

    // for speed, just respect the bounds, and disable AA. May give us a few
    // false positives and negatives.
    virtual bool clipPath(const SkPath& path, SkRegion::Op op,
                          bool doAA) SK_OVERRIDE {
        return this->INHERITED::clipRect(path.getBounds(), op, false);
    }

private:
    typedef SkCanvas INHERITED;
};

SkData* SkPictureUtils::GatherPixelRefs(SkPicture* pict, const SkRect& area) {
    if (NULL == pict) {
        return NULL;
    }

    // this test also handles if either area or pict's width/height are empty
    if (!SkRect::Intersects(area,
                            SkRect::MakeWH(SkIntToScalar(pict->width()),
                                           SkIntToScalar(pict->height())))) {
        return NULL;
    }

    SkTDArray<SkPixelRef*> array;
    PixelRefSet prset(&array);

    SkBitmap emptyBitmap;
    emptyBitmap.setConfig(SkBitmap::kARGB_8888_Config, pict->width(), pict->height());
    // note: we do not set any pixels (shouldn't need to)

    GatherPixelRefDevice device(emptyBitmap, &prset);
    NoSaveLayerCanvas canvas(&device);

    canvas.clipRect(area, SkRegion::kIntersect_Op, false);
    canvas.drawPicture(*pict);

    SkData* data = NULL;
    int count = array.count();
    if (count > 0) {
        data = SkData::NewFromMalloc(array.detach(), count * sizeof(SkPixelRef*));
    }
    return data;
}
<commit_msg>Accelerate handling of clipRRect in calls to SkPictureUtils::GatherPixelRefs<commit_after>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkPictureUtils.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDevice.h"
#include "SkPixelRef.h"
#include "SkShader.h"
#include "SkRRect.h"

class PixelRefSet {
public:
    PixelRefSet(SkTDArray<SkPixelRef*>* array) : fArray(array) {}

    // This does a linear search on existing pixelrefs, so if this list gets big
    // we should use a more complex sorted/hashy thing.
    //
    void add(SkPixelRef* pr) {
        uint32_t genID = pr->getGenerationID();
        if (fGenID.find(genID) < 0) {
            *fArray->append() = pr;
            *fGenID.append() = genID;
//            SkDebugf("--- adding [%d] %x %d\n", fArray->count() - 1, pr, genID);
        } else {
//            SkDebugf("--- already have %x %d\n", pr, genID);
        }
    }

private:
    SkTDArray<SkPixelRef*>* fArray;
    SkTDArray<uint32_t>     fGenID;
};

static void not_supported() {
    SkASSERT(!"this method should never be called");
}

static void nothing_to_do() {}

/**
 *  This device will route all bitmaps (primitives and in shaders) to its PRSet.
 *  It should never actually draw anything, so there need not be any pixels
 *  behind its device-bitmap.
 */
class GatherPixelRefDevice : public SkDevice {
private:
    PixelRefSet*  fPRSet;

    void addBitmap(const SkBitmap& bm) {
        fPRSet->add(bm.pixelRef());
    }

    void addBitmapFromPaint(const SkPaint& paint) {
        SkShader* shader = paint.getShader();
        if (shader) {
            SkBitmap bm;
            // Check whether the shader is a gradient in order to short-circuit
            // call to asABitmap to prevent generation of bitmaps from
            // gradient shaders, which implement asABitmap.
            if (SkShader::kNone_GradientType == shader->asAGradient(NULL) &&
                shader->asABitmap(&bm, NULL, NULL)) {
                fPRSet->add(bm.pixelRef());
            }
        }
    }

public:
    GatherPixelRefDevice(const SkBitmap& bm, PixelRefSet* prset) : SkDevice(bm) {
        fPRSet = prset;
    }

    virtual void clear(SkColor color) SK_OVERRIDE {
        nothing_to_do();
    }
    virtual void writePixels(const SkBitmap& bitmap, int x, int y,
                             SkCanvas::Config8888 config8888) SK_OVERRIDE {
        not_supported();
    }

    virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
                            const SkPoint[], const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawRect(const SkDraw&, const SkRect&,
                          const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawOval(const SkDraw&, const SkRect&,
                          const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawPath(const SkDraw&, const SkPath& path,
                          const SkPaint& paint, const SkMatrix* prePathMatrix,
                          bool pathIsMutable) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
                            const SkIRect* srcRectOrNull,
                            const SkMatrix&, const SkPaint&) SK_OVERRIDE {
        this->addBitmap(bitmap);
    }
    virtual void drawBitmapRect(const SkDraw&, const SkBitmap& bitmap,
                                const SkRect* srcOrNull, const SkRect& dst,
                                const SkPaint&) SK_OVERRIDE {
        this->addBitmap(bitmap);
    }
    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
                            int x, int y, const SkPaint& paint) SK_OVERRIDE {
        this->addBitmap(bitmap);
    }
    virtual void drawText(const SkDraw&, const void* text, size_t len,
                          SkScalar x, SkScalar y,
                          const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
                             const SkScalar pos[], SkScalar constY,
                             int, const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
                                const SkPath& path, const SkMatrix* matrix,
                                const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
                              const SkPoint verts[], const SkPoint texs[],
                              const SkColor colors[], SkXfermode* xmode,
                              const uint16_t indices[], int indexCount,
                              const SkPaint& paint) SK_OVERRIDE {
        this->addBitmapFromPaint(paint);
    }
    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
                            const SkPaint&) SK_OVERRIDE {
        nothing_to_do();
    }

protected:
    virtual bool onReadPixels(const SkBitmap& bitmap,
                              int x, int y,
                              SkCanvas::Config8888 config8888) SK_OVERRIDE {
        not_supported();
        return false;
    }
};

class NoSaveLayerCanvas : public SkCanvas {
public:
    NoSaveLayerCanvas(SkDevice* device) : INHERITED(device) {}

    // turn saveLayer() into save() for speed, should not affect correctness.
    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
                          SaveFlags flags) SK_OVERRIDE {

        // Like SkPictureRecord, we don't want to create layers, but we do need
        // to respect the save and (possibly) its rect-clip.

        int count = this->INHERITED::save(flags);
        if (bounds) {
            this->INHERITED::clipRectBounds(bounds, flags, NULL);
        }
        return count;
    }

    // disable aa for speed
    virtual bool clipRect(const SkRect& rect, SkRegion::Op op,
                          bool doAA) SK_OVERRIDE {
        return this->INHERITED::clipRect(rect, op, false);
    }

    // for speed, just respect the bounds, and disable AA. May give us a few
    // false positives and negatives.
    virtual bool clipPath(const SkPath& path, SkRegion::Op op,
                          bool doAA) SK_OVERRIDE {
        return this->INHERITED::clipRect(path.getBounds(), op, false);
    }
    virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op,
                           bool doAA) SK_OVERRIDE {
        return this->INHERITED::clipRect(rrect.getBounds(), op, false);
    }

private:
    typedef SkCanvas INHERITED;
};

SkData* SkPictureUtils::GatherPixelRefs(SkPicture* pict, const SkRect& area) {
    if (NULL == pict) {
        return NULL;
    }

    // this test also handles if either area or pict's width/height are empty
    if (!SkRect::Intersects(area,
                            SkRect::MakeWH(SkIntToScalar(pict->width()),
                                           SkIntToScalar(pict->height())))) {
        return NULL;
    }

    SkTDArray<SkPixelRef*> array;
    PixelRefSet prset(&array);

    SkBitmap emptyBitmap;
    emptyBitmap.setConfig(SkBitmap::kARGB_8888_Config, pict->width(), pict->height());
    // note: we do not set any pixels (shouldn't need to)

    GatherPixelRefDevice device(emptyBitmap, &prset);
    NoSaveLayerCanvas canvas(&device);

    canvas.clipRect(area, SkRegion::kIntersect_Op, false);
    canvas.drawPicture(*pict);

    SkData* data = NULL;
    int count = array.count();
    if (count > 0) {
        data = SkData::NewFromMalloc(array.detach(), count * sizeof(SkPixelRef*));
    }
    return data;
}
<|endoftext|>