Как использовать graphics из нативного кода
От: goldfish Украина  
Дата: 26.10.16 18:30
Оценка: 46 (1)
Здравствуйте Андрей
Где взять HGFX gfx, который передаётся конструктору?
sciter::graphics gfx(HGFX gfx);

А ещё на страничке Скайтера есть красивые скриншоты: http://sciter.com/htmlcss-ui-in-medicine/
Вы не в курсе, эти графики рисовались tiscript'ом или из нативного кода?
Вообще здорово графики выглядят
Re: Как использовать graphics из нативного кода
От: c-smile Канада http://terrainformatica.com
Дата: 26.10.16 20:24
Оценка:
Здравствуйте, goldfish, Вы писали:

G>Здравствуйте Андрей

G>Где взять HGFX gfx, который передаётся конструктору?
G>sciter::graphics gfx(HGFX gfx);

sdk/include/behaviors/behavior_drawing.cpp
struct native_clock: public event_handler {

 virtual bool handle_draw   (HELEMENT he, DRAW_PARAMS& params ) 
    {
      if( params.cmd != DRAW_CONTENT ) return false; // drawing only content layer

      ... 

      sciter::graphics gfx(params.gfx);
      gfx.state_save();

      ...

}


G>А ещё на страничке Скайтера есть красивые скриншоты: http://sciter.com/htmlcss-ui-in-medicine/

G>Вы не в курсе, эти графики рисовались tiscript'ом или из нативного кода?
G>Вообще здорово графики выглядят

Я попросил автора поделиться опытом.

Отредактировано 26.10.2016 20:25 c-smile . Предыдущая версия .
Re: Как использовать graphics из нативного кода
От: c-smile Канада http://terrainformatica.com
Дата: 27.10.16 04:52
Оценка: 2 (1)
Здравствуйте, goldfish, Вы писали:

G>Вы не в курсе, эти графики рисовались tiscript'ом или из нативного кода?

G>Вообще здорово графики выглядят

Вот ответ автора:

Приветствую.
1) Все представленные графики можно отрисовать на движке sciter в его фронт части, т.е на tis. "Красота" графиков это прежде всего красота данных, в данном случае красота природы работы человеческого сердца. У вас должны быть "красивые" данные, мы их получали, естественно, из бекенд части, т.е. из С++.

2) Со скоростью отрисовки готовых данных проблем у tis нет, но в некоторых случаях (не во всех) нужно было создавать различные буферы. В частности я делал буфер для уже готовых участков gfx.path, чтобы не перерисовывать картинку для каждого чиха. В некоторых случаях в качастве буфера использовали Image, и просто смотрели, нужно его перерисовывать или нет. (Изменились размеры или нет, изменились данные или нет, изменилось смещение или нет.. )

3) Нюансы.
Если данные имеют дискретную природу, рисуйте дискретно. Т.е. если график это набор вертикальных линий шириной в int пикселов, то и рисуйте их в точках x, где x — int, а не float. В tis я перед отрисовкой вообще всегда смещал общую сетку на 0.5, чтобы линии рисовались четко в интовых положениях ( там есть особенность по умолчанию рисовать между двумя физическими пикселами, т.е в итоге линии получаются размытыми) Если стоит
При отрисовке "мелкого" экг было больше всего проблем как с "красотой" так и с производительностью. Там большие массивы данных. В итоге на сях сделали алгоритм поиска экстремумов, и рисовали линии не сглаженные) именно между ними. И рисовали полностью уже на сях, но это правда после алгоритма поиска экстремумов думаю не принципиально, сама по себе отрисовка относительно быстрая, проблема в обработке данных.

Re[2]: Как использовать graphics из нативного кода
От: goldfish Украина  
Дата: 27.10.16 17:32
Оценка:
Спасибо Андрею и автору
а есть ограничение на количество элементов в патче?
сетку смещали на 0,5 с помощью translate?
Re[3]: Как использовать graphics из нативного кода
От: NNektov  
Дата: 27.10.16 19:13
Оценка:
Здравствуйте, goldfish, Вы писали:

G>а есть ограничение на количество элементов в патче?

*
Патч ? Кол-во элементов в Graphics.Path() наверное. Я с такими ограничениями не сталкивался, полагаю,
path.moveTo() перед gfx.drawPath можно вызывать ну очень много раз. Вопрос в том, зачем это делать
слишком много раз: рисовать нужно не менее, чем необходимо, но и не более того. В смысле, не видимые пользователю
участки больших графиков в path загонять, конечно, не стоит.

G>сетку смещали на 0,5 с помощью translate?

*
да.
Re: Как использовать graphics из нативного кода
От: goldfish Украина  
Дата: 28.10.16 09:53
Оценка:
Здравствуйте
Откуда берутся лишние пикселы в координатах right и bottom?
скайтер 3.3.2.12
virtual bool handle_draw(HELEMENT he, DRAW_PARAMS& params){

    if (params.cmd != DRAW_CONTENT) return false; // drawing only content layer

    sciter::graphics gfx(params.gfx);
    printf("rect1 %i %i %i %i\n", params.area.left, params.area.top, params.area.right, params.area.bottom);

    RECT rect = dom::element(he).get_location(CONTENT_BOX);
    printf("rect11 %i %i %i %i\n", rect.left, rect.top, rect.right, rect.bottom);

в результате получаем вот что:
rect1 9 336 781 553
rect11 9 336 782 554
Re[2]: Как использовать graphics из нативного кода
От: c-smile Канада http://terrainformatica.com
Дата: 28.10.16 18:29
Оценка:
Здравствуйте, goldfish, Вы писали:

G>Здравствуйте

G>Откуда берутся лишние пикселы в координатах right и bottom?

Упс...

element::get_location() возвращает лишний пиксель... Поправлю.

В params.area — актуальное значение.
Re[2]: Как использовать graphics из нативного кода
От: goldfish Украина  
Дата: 29.10.16 12:27
Оценка:

У вас должны быть "красивые" данные, мы их получали, естественно, из бекенд части, т.е. из С++.

Здравствуйте
Как рационально передавать данные из нативного кода в tiscript, как минимизировать накладные расходы при передаче?
Если для каждой вершины вызывается функция из нативного кода, это правильно?
Как можно ускорить передачу?

tiscript:
function drawChart(gfx)
{
    var x1, y1, x2, y2, pos;
        
    gfx.pushLayer(#client-box);
        
    pos = get_data(0);
    x1 = pos[0];
    y1 = pos[1];
        
    for(var n = 1; n < count; n++)
    {
        pos = get_data(n);
        x2 = pos[0];
        y2 = pos[1];
            
        gfx.line( x1, y1, x2, y2 );
    
        x1 = x2;
        y1 = y2;
    }        
}

нативный код:
tiscript::value TISAPI get_data_native(tiscript::VM* vm)
{
    int index;

    tiscript::args(vm) >> tiscript::args::skip >> tiscript::args::skip >> index;

    tiscript::value arr_dataXY = tiscript::create_array(vm, 2);

    tiscript::set_elem( vm, arr_dataXY, 0, tiscript::v_float(data[index * 2]) );
    tiscript::set_elem( vm, arr_dataXY, 1, tiscript::v_float(data[index * 2 + 1]) );

    return arr_dataXY;
}
static tiscript::method_def get_data_md("get_data", get_data_native);
Отредактировано 29.10.2016 12:32 goldfish . Предыдущая версия .
Re[3]: Как использовать graphics из нативного кода
От: NNektov  
Дата: 29.10.16 15:48
Оценка: +1
G>Как рационально передавать данные из нативного кода в tiscript, как минимизировать накладные расходы при передаче?
G>Если для каждой вершины вызывается функция из нативного кода, это правильно?
*
Понятно, что N вызовов tis -> с++ по 1 кусочку данных хуже, чем 1 вызов массива или объекта из N кусков данных.
Накладные расходы на вызовы методов и внутренние проверки корректности данных (если, конечно, они есть).
Т.е. имеет смысл 1 раз забирать массив данных, и в tis бегать уже по этому массиву.

Как можно ускорить передачу?
*
Кроме варианта с массивами, можно устранить сам факт передачи данных, т.е. максимальное кол-во
работы с данными и саму отрисовку — все поместить в нативный код, а в tis оставить все "врапперы", обработки событий
и всякое наследуемое общее для других модулей и бехавиоров.

Т.е я для особенно требующих производительности модулей писал связку:
sciter behavior на с++ (есть в примерах кода) + behavior (наследующий несколько общих для проекта методов, чтобы не повторять код) на tis.
Отредактировано 29.10.2016 15:58 NNektov . Предыдущая версия .
Re[3]: Как использовать graphics из нативного кода
От: NNektov  
Дата: 29.10.16 15:57
Оценка:
Кроме того, в нашем случае больше всего времени уходило на пробежки по массивам.
Представьте себе — ~ 100000 раз пройтись раза три: сначала получить данные, потом подгововить конкретно для текущего модуля (100 000 тут становится меньшим числом, но тем не менее),
потом еще раз бегать и рисовать.
В c++ бехавиоре я сократил эту пробежку до 2-х. Т.е. подготавливаю в цикле участок и сразу внутри этого цикла вызываю отрисовку линии.
Какие-то случаи можно сокращать и до 1-го цикла.
Отредактировано 29.10.2016 15:58 NNektov . Предыдущая версия .
Re[3]: Как использовать graphics из нативного кода
От: c-smile Канада http://terrainformatica.com
Дата: 29.10.16 18:30
Оценка:
Здравствуйте, goldfish, Вы писали:

G>

G>У вас должны быть "красивые" данные, мы их получали, естественно, из бекенд части, т.е. из С++.

G>Здравствуйте
G>Как рационально передавать данные из нативного кода в tiscript, как минимизировать накладные расходы при передаче?
G>Если для каждой вершины вызывается функция из нативного кода, это правильно?
G>Как можно ускорить передачу?

Ну рисовать polygons или polylines путем вызовов gfx.line это неправильно по любому.
От native надо запрашивать массив точек.

function createChartPath()

  var positions = get_data_vector(0);
  var path = new Graphics.Path();

  path.moveTo(positions[0],positions[1]);

  var count = positions.length;
  for(var n = 2; n < count; n += 2)
     path.lineTo( positions[n],positions[n+1] );

  return path;
}


Этот path есть cacheable object т.е. его надо создавать только если данные изменились.

При отрисовке использовать уже готовый raphics.Path объект.

var path = ...;
...
if( !path )
  path = createChartPath();

gfx.drawPath(path);


Надо помнить что WM_PAINT могу приходить чаще чем данные меняются. Например при анимациях где-то в стороне.
Совсем не обязательно в этом случае по новой path строить.

Дело в том что GPU фактически рисует только треугольники.
Для вывести на экран линию, её нужно сначала tesselate.
Вот Graphics.Path внутри хранит этот triangle mesh.
А в paint этот mesh просто закидывается в GPU. А тот уже быстро рисует треугольники.
Re[3]: Как использовать graphics из нативного кода
От: goldfish Украина  
Дата: 29.10.16 19:15
Оценка:
Спасибо всем за ответы
Если графики рисовать в текстуру только когда нужно, после чего выводить текстуру в окне скайтера и уже поверх неё рисовать всё остальное (оси координат, легенду , rect tracker и т.д).
tiscript:
function paint(gfx)
{
    // если графики изменились то рисуем их в imageChart
    if(invalidChart)
    {
        if(imageChart) imageChart.destroy();
            
        imageChart = new Image(width, height, drawChart);
        invalidChart = false;
    }

    gfx.drawImage(imageChart, 0.0, 0.0);
        
    // рисуем всё остальное
    ...
}

function drawChart(gfx)
{
    ...
}

Как поместить drawChart в нативный код?
drawChart вызывается из tiscript при необходимости, заполняет текстуру, а уже она выводится в tiscript
Re[4]: Как использовать graphics из нативного кода
От: c-smile Канада http://terrainformatica.com
Дата: 30.10.16 02:49
Оценка:
Здравствуйте, goldfish, Вы писали:

В следующей версии ( 3.3.2.13 ) я добавляю в API функции которые позволят передавать графические объекты из script в native и обратно.

Плюс добавляется метод рисования на image средствами sciter::graphics:
sciter::image::paint( std::function<void(sciter::graphics& gfx)> painter );



Пример передачи графических объектов, script:

function paint(gfx) {
  
  this.nativeDrawGrid(gfx);

  var path = this.nativeGetChartPath();
  gfx.drawPath(path);
}


и в native

struct native_chart: public event_handler {
  
  sciter::value nativeGetChartPath() {
     sciter::path p = sciter::path::create();
     p.move_to(0,0);
     for(int n = 0; n < count; n+= 2)
       p.line_to(points[n],points[n+1]);
     return p.to_value(); // wrap the path into sciter::value;
  }

  sciter::value nativeDrawGrid(sciter::value vGfx) {
     sciter::graphics gfx = sciter::graphics::from(vGfx); // fetch gfx from envelope 
     
     ... use sciter::graphics methods here ...
  }

  BEGIN_FUNCTION_MAP
     FUNCTION_0("nativeGetChartPath", nativeGetChartPath); 
     FUNCTION_1("nativeDrawGrid", nativeDrawGrid);
  END_FUNCTION_MAP

};


Т.е. рисуйте где хотите/нужно.
Re: Как использовать graphics из нативного кода
От: goldfish Украина  
Дата: 03.11.16 06:01
Оценка:
Здравствуйте Андрей
Спасибо за обновление скайтера
вопрос такой:
у меня в нативном коде функция native_draw следующего вида
tiscript::value TISAPI native_draw(tiscript::VM* vm)
{
    ...
    // creating path:
    sciter::path p = sciter::path::create();
    ...

    return p.to_value(); // wrap the path into sciter::value;
}
static tiscript::method_def native_draw_md("native_draw", native_draw);

она возвращает tiscript::value а вернуть нужно патч в виде sciter::value
что делать?

и ещё вопрос
в скрипте:
var imageChart = new Image(width, height, drawChart);
view.nativeImage(imageChart);

в нативном коде:
void nativeImage(const sciter::value im)
{
    static sciter::image img;
    img.from(im);

    if (!img.is_valid()) printf("image is not valid!\n");

    UINT width, height;
    img.dimensions(width, height);
    printf("nativeImage %u %u\n", width, height);
}

image не валидный, не пойму почему

и ещё
такой код работает кроме img.clear(), он завершается своим assert
sciter::value nativeImage()
{
    static sciter::image img;
    img = sciter::image::create(100, 100, false);

    if (!img.is_valid())
        printf("image is not valid!\n");

//    img.clear(sciter::gcolor(100, 0, 0));

    return img.to_value();
}
Отредактировано 06.11.2016 2:16 goldfish . Предыдущая версия . Еще …
Отредактировано 04.11.2016 8:16 goldfish . Предыдущая версия .
Отредактировано 03.11.2016 20:07 goldfish . Предыдущая версия .
Re: Как использовать graphics из нативного кода
От: goldfish Украина  
Дата: 06.11.16 22:48
Оценка: 48 (1)
Здравствуйте Андрей
Код приведенный ниже должен вернуть в скрипт изображение содержащее красную линию на сером фоне.
Однако получается красная линия на чёрном фоне. Если закоментировать img.paint(&painter) то получается изображение с серым фоном (без красной линии естественно). Когда начинает работать painter фон закрашивается чёрным
Вопрос — как залить всё изображение нужным цветом не прибегая к gfx.rectangle(0, 0, width, height)?
img.clear(sciter::gcolor(127, 127, 127)) у меня не заработал (смотри предыдущее сообщение)

class IMGPainter : public sciter::painter {
public:
void paint(sciter::graphics& gfx, UINT width, UINT height)
{
    gfx.line_width(1);
    gfx.line_color(sciter::gcolor(255, 0, 0));
    gfx.line(0, 0, width, height);
}
};
sciter::value nativeImage(const UINT width, const UINT height)
{
    BYTE *b = new BYTE[width * height * 4];
    memset(b, 127, width * height * 4);

    sciter::image img;
    img = sciter::image::create(width, height, false, b);

    IMGPainter painter;
    img.paint(&painter);

    return img.to_value();
}

tiscript:
var imageChart = view.nativeImage(width, height);
gfx.drawImage(imageChart, 0.0, 0.0);


и ещё один вопрос: есть в нативном коде аналог скриптового image.destroy()?
Отредактировано 07.11.2016 23:15 goldfish . Предыдущая версия . Еще …
Отредактировано 06.11.2016 22:55 goldfish . Предыдущая версия .
Re[2]: Как использовать graphics из нативного кода
От: c-smile Канада http://terrainformatica.com
Дата: 09.11.16 02:13
Оценка:
Здравствуйте, goldfish, Вы писали:

r4521 — [api, graphics] sciter::image::paint() preserves original image allowing incremental updates.

G>и ещё один вопрос: есть в нативном коде аналог скриптового image.destroy()?


Он там не нужен.

sciter::image это smart pointer, если ты его нигде не сохрагишь или не передашь в script то он уничтожится.
Re[3]: Как использовать graphics из нативного кода
От: goldfish Украина  
Дата: 09.11.16 06:18
Оценка:
Здравствуйте, c-smile, Вы писали:
CS>sciter::image это smart pointer, если ты его нигде не сохрагишь или не передашь в script то он уничтожится.
в скрипте при изменении размеров окна я делал так:
var image = null;
function paint(gfx)
{
...
    if(imageNeedResize) // если изменились размеры окна
    {
        if(image) image.destroy(); // удаляем старую картинку
        image = new Image(width, height, drawChart); // рисуем новую картинку
    }
    gfx.drawImage(image, 0.0, 0.0);

а в нативном коде так:
image img;
virtual bool handle_draw(HELEMENT he, DRAW_PARAMS& params)
{
...
    if (imageNeedResize)
        img = image::create(width, height, false);

    gfx.draw_image(&img, 0.0, 0.0);

Не удалив изображения со старыми размерами мы создаём его с новыми. Не произойдёт утечки памяти?
Отредактировано 09.11.2016 6:29 goldfish . Предыдущая версия .
Re[4]: Как использовать graphics из нативного кода
От: c-smile Канада http://terrainformatica.com
Дата: 09.11.16 06:34
Оценка:
Здравствуйте, goldfish, Вы писали:


        img = image::create(width, height, false);


G>Не удалив изображения со старыми размерами мы создаём его с новыми. Не произойдёт утечки памяти?


 image& operator = (const image& im) 
    { 
      if(himg) gapi()->imageRelease(himg); // <<<<<<<<<<<<<<<<<<
      himg = im.himg; if(himg) gapi()->imageAddRef(himg); 
      return *this;
    }
Отредактировано 09.11.2016 19:38 c-smile . Предыдущая версия .
Re: Как использовать graphics из нативного кода
От: goldfish Украина  
Дата: 11.11.16 12:36
Оценка:
Здравствуйте
Как повторить в нативном коде такую штуку из tiscript:

function f(){
    var x = ...
    var y = ...
    return (x, y);
}
var (a, b) = f();

Если возвращать массив из двух значений то a и b будут содержать этот массив
sciter::value f(){
    sciter::value result;

    result.append(x);
    result.append(y);

    return result;
}

var (a, b) = view.f();
Отредактировано 11.11.2016 12:39 goldfish . Предыдущая версия .
Re[2]: Как использовать graphics из нативного кода
От: c-smile Канада http://terrainformatica.com
Дата: 12.11.16 04:09
Оценка:
Здравствуйте, goldfish, Вы писали:

G>Здравствуйте

G>Как повторить в нативном коде такую штуку из tiscript:

var (a, b) = view.f();


Пока никак (если пользовать sciter::value API).

Подождать когда я сделаю destructuring assignment в стиле ES6:
var [a, b] = view.f();


тогда возвращая массив можно будет его разложить по переменным.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.