На главную... Продукты | Технологии | Классификаторы | Проекты | Скачать | Цены| Форум | Статьи | Обучение | Контакты

Артефакты при рисовании карт из разных потоков

Поиск  Пользователи  Правила  Войти
Форум » Linux » Средства разработки ГИС-приложений для Linux
Страницы: Пред. 1 2 3 4 След.
RSS
Артефакты при рисовании карт из разных потоков
 
Использовать принтерно-растровый вид и компоненты для отображения карты средствами Qt.
 
Дошли руки задать вопрос, который давно интересует.
При скроле карты подрисовываю только новые участки карты. То есть, если листаем карту из нижнего правого угла в верхний левый, то основную часть изображения не меняя переношу в левый верхний угол, а справа и снизу подрисовываю 2 прямоугольника шириной и высотой в соответствий с тем на сколько передивинули карту. Всё прекрасно подрисовывается, когда делаю это так:
Код
RECT rectDraw {rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1};
mapPaintToXImage(hmap, &ximagedesc, 0, 0, &rectDraw);
где rect - QRect, описывающий требуемый для подрисовки прямоугольник карты.
Однако картина меняется, когда хочется рисовать эти 2 прямоугольника одновременно в разных потоках. В этом случае приходится делать так:
Код
DFRAME frame;
frame.X1 = rect.left(), frame.Y1 = rect.top(), frame.X2 = rect.right() + 1, frame.Y2 = rect.bottom() + 1;
mapPictureToPlane(hmap, &frame.X1, &frame.Y1);
mapPictureToPlane(hmap, &frame.X2, &frame.Y2);
HPAINT hpaint = mapCreatePaintControl(hmap);
mapPaintByFrameToXImagePro(hmap, &ximagedesc, 0, &frame, rect.width(), rect.height(), 0, 0, hpaint);
mapFreePaintControl(hpaint);
Изображение карты получается таким.
Вертикальные линии в нижней четверти "дрожат" после скрола. То есть, подрисованные прямоугольники как будто на 1 пиксель "съезжают" вправо или влево относительно остального изобрадения. Может ли это быть из-за того, что координаты карты из пикселей переводятся в метры и из-за этого получается такая погрешность?
Как это можно исправить?
Использую только MAPAPI. Astra Linux 1.5. Такая проблема проявляется как в экранном, так и в принтерном типах отображения.  В версии 12.0.2 то же самое.
Код
// Версия библиотеки MapAccess
// (если применяется "mapacces.h" и т.п.)
#define MAPACCESSVERSION  20160605

// Версия интерфейса MAPAPI и ядра библиотеки
#define MAPAPIVERSION  0x0111304
Изменено: Tacio - 30.05.2017 17:08:11
 
Приветствую!

В своем приложении рисую при помощи mapPaintToXImage, в координатах изображения таких проблем не замечал.
Единственное, рисую бОльшими фрагментами чем нужно, т.к. нет смысла особого рисовать полосками по 2-3 пикселя, все равно, как правило, пользователь дальше будет прокручивать карту.
Т.е. если понадобилось отрисовать нижнюю полосу высотой в 5 пикселей, Запрашивается изображение высотой, например, 256 и при дальнейшей прокрутке не требуется вызывать mapPaint*

С уважением,
Матвеев П.В.
 
Павел, при рисовании с помощью mapPaintToXImage у меня тоже проблем нет. Проблема проявляется только при использовании mapPaintByFrameToXImagePro. Причём как в однопоточном, так и в многопоточном режиме.
У меня на карте изменчивая обстановка, поэтому к тому времени как пользователь пролистнёт карту к тем самым заранее отрисованным пикселяем, обстановка может поменяться, а я этого не отображу....
 
Приветствую!

Ну если у вас изменчивая обстановка, то и необходимость отрисовки только части области не так велика. Вероятно вы все равно перерисовываете всю видимую область?
У меня обстановка тоже меняется и я храню timestamp этого изменения, если ничего не менялось, беру изображение из кэша, если менялось - перерисовываю.

С уважением,
Матвеев П.В.
 
Вся видимая в данный момент область перерисовывается только если изменилась обстановка внутри этой области, либо если изменился масштаб. Дальше при скролле (или ресайзе) подрисовываются небольшие прямоугольники в соответствии с величиной скрола - обычно этот процесс очень быстрый: 3-5 мс. Держать кэш в данном случае не вижу смысла. Но на некоторых картах или в принтерном виде время подрисовки прямоугольников возрастает до 60-100мс в зависимости от масштаба, и скролл уже совсем не плавный. Вот для этих случаев и хочется эти 2 прямоугольника рисовать одновременно.
 
Возможно, что проблема в логике склеивания изображения?
Не могли бы вы предоставить тестовый пример, демонстрирующий некорректное поведение?
 
Логика склеивания изображения в обоих случаях одинаковая. Разница лишь в этой функции:
Код
//rect - прямоугольник, который надо нарисовать;
static long paintToXIMage(HMAP hmap, char* pixels, const QRect& rect, uint8_t depth) {
    const size_t stride = ((rect.width() * depth) + 7) / 8;

    XIMAGEDESC ximagedesc;
    ximagedesc.Depth     = depth; // Размер элемента в битах (8,15,16,24,32)
    ximagedesc.CellSize  = depth / 8; //Размер элемента(пиксела) в байтах
    ximagedesc.Point     = pixels; // Адрес начала области пикселов
    ximagedesc.Width     = rect.width();
    ximagedesc.Height    = rect.height();
    ximagedesc.RowSize   = stride; //ширина строки в байтах

#ifdef USE_THREADS
    DFRAME frame;
    frame.X1 = rect.left(), frame.Y1 = rect.top(), frame.X2 = rect.right() + 1, frame.Y2 = rect.bottom() + 1;
    mapPictureToPlane(hmap, &frame.X1, &frame.Y1);
    mapPictureToPlane(hmap, &frame.X2, &frame.Y2);


    HPAINT hpaint = mapCreatePaintControl(hmap);
    long rc = mapPaintByFrameToXImagePro(hmap, &ximagedesc, 0, &frame, rect.width(), rect.height(), 0, 0, hpaint);
    mapFreePaintControl(hpaint);
    return rc;
#else
    RECT rectDraw {rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1};
    return mapPaintToXImage(hmap, &ximagedesc, 0, 0, &rectDraw);
#endif
которая вызывается либо последовательно, либо параллельно, если указана директива USE_THREADS:
Код
...
    QPainter p(&image); //неизменившееся изображение

    QVector<size_t> buffers;
    buffers.reserve(parts_to_draw.size());
    size_t buff_offset = 0;
    //определяем смещения в буфере для каждого прямоугольника
    //parts_to_draw - Qvector<QRect> прямоугольников, которые нужно нарисовать относительно неизменившегося изображения;
    for(const QRect& rect :  parts_to_draw) {
        const size_t stride = ((parts_to_draw.width() * depth) + 7) / 8;
        buffers.push_back(buff_offset);
        buff_offset += stride * rect.height();
    }

#ifdef USE_THREADS
    #pragma omp parallel for num_threads(2) schedule(dynamic,1)
#endif
    for(int i = 0; i < parts_to_draw.size(); ++i) {
        paintToXIMage(hMap, buffer + offsets[i], parts_to_draw[i], depth);
    }

    for(int i = 0; i < parts_to_draw.size(); ++i) {
        const size_t stride = ((parts_to_draw.width() * depth) + 7) / 8;
        //готовая картинка для подрисовки
        const QImage part(reinterpret_cast<const uint8_t*>(buffer + offsets[i]), parts_to_draw[i].width(), parts_to_draw[i].height(), stride, imageFormat(depth));
        //подрисовываем картинку part, смещённую на dx и dy, в общее не изменившееся изображение
        p.drawImage(parts_to_draw[i].translated(-dxy), part, part.rect());
    }

    p.end();
...

Больше в коде отличий нет.

Заметил, что при активной директиве USE_THREADS "дрожат" только вертикальные линии при подрисовке, с горизонтальными всегда всё в порядке. Без активной директивы USE_THREADS всегда всё норм.
Изменено: Tacio - 01.06.2017 10:44:35
 
Код для поиска прямоугольников для подрисовки:
Код
//текущее изображение карты
QImage mapImage;

....... Смещаем карту

//величина смещения изображения
const QPoint dxy(dx,dy);

//текуще изображение
const QRect imgRect = mapImage.rect();

//изображение после скрола
const QRect scrolled_rect = imgRect.translated(dxy);

//находим ту часть изображения, которая не изменится при скролле (только сместится)
const QRegion region = QRegion(imgRect).xored(scrolled_rect);

QVector<QRect> rects_to_draw;
//находим прямоугольники, которые нужно подрисовать
for(const QRect& rect : region.rects()) {
    if(!imgRect.contains(rect)) {
        rects_to_draw.push_back(rect);
    }
}

//оставляем неизменившуся часть изображения
QImage scrolledImage = mapImage.copy(scrolled_rect);
QPainter p(&scrolledImage);
//подрисовываем к ней новые треугольники
//дальше идёт код, представленный в сообщениях выше
...
p.end();

mapImage = scrolledImage;

 
 
Упростил пример, чтобы проверять отдельно рисование.
Оставил использование openmp, чтобы попробовать :)
В таком виде отрисовал карту Ногинск. По рамке искажений нет.
Опять же, желательно не выделять на каждое рисование hPaint, потому что это дополнительное время на выделение/освобождение внутренних буферов.

Код
void PaintLineToXImage( HMAP hMap, uchar* linePoint, long rowNum, long rowWidth, RECT * rect )
{
  XIMAGEDESC imageDesc;
  imageDesc.Width = rowWidth;
  imageDesc.Height = 1;
  imageDesc.Depth = 32;
  imageDesc.CellSize = imageDesc.Depth / 8;
  imageDesc.RowSize = imageDesc.CellSize * imageDesc.Width;
  imageDesc.Point = (char*)linePoint;

  DFRAME frame;
  frame.X1 = rect->left;
  frame.Y1 = rect->top;
  frame.X2 = rect->right;
  frame.Y2 = rect->bottom;
  mapPictureToPlane( hMap, &frame.X1, &frame.Y1 );
  mapPictureToPlane( hMap, &frame.X2, &frame.Y2 );

  HPAINT hPaint = mapCreatePaintControl( hMap );
  mapPaintByFrameToXImagePro( hMap, &imageDesc, 0, &frame, rowWidth, 1, 0, 0, hPaint );
  mapFreePaintControl( hPaint );

//  mapPaintToXImage( hMap, &imageDesc, 0, 0, rect );
}

void PaintByLines( HMAP hMap )
{
  uchar * imageBuffer = (uchar*)AllocateTheMemory( 2048 * 2048 * 4 );

#pragma omp parallel for num_threads(2) schedule(dynamic,1)
  for ( long i = 0; i < 2048; i++ )
  {
    // Setup rect to 1 line
    RECT rect;
    rect.left = 0;
    rect.right = 2048;
    rect.top = i;
    rect.bottom = i + 1;
    PaintLineToXImage( hMap, &imageBuffer[2048 * 4 * i], i, 2048, &rect);
    rect.top++;
    rect.bottom++;
  }

  QImage image( (uchar*)imageBuffer, 2048, 2048, QImage::Format_RGB32);
  image.save( "/tmp/2.png" );

  FreeTheMemory( (char*)imageBuffer );
}
Страницы: Пред. 1 2 3 4 След.
Читают тему (гостей: 1)



© КБ Панорама, 1991-2024

Регистрируясь или авторизуясь на форуме, Вы соглашаетесь с Политикой конфиденциальности