487 строки
16 KiB
C++
487 строки
16 KiB
C++
/*
|
|
Copyright (c) 2024 SWD Embedded Systems, Ltd. <http://www.kpda.ru>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
software and associated documentation files (the "Software"), to deal in the Software
|
|
without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
to whom the Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all copies or
|
|
substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM,
|
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include <QVector>
|
|
|
|
#include <common_math.h>
|
|
#include <data_buffer.h>
|
|
#include <data_descriptor.h>
|
|
#include <databuffer_draw.h>
|
|
#include <gis_data_engine.h>
|
|
#include <gishelper_profiler.h>
|
|
#include <gishelper_service.h>
|
|
#include <gis_raw_internal.h>
|
|
#include <databuffer_draw.h>
|
|
#include <mdp_internal.h>
|
|
#include <renderbuffer_desc.h>
|
|
|
|
#define RB_VECTOR_PREFIX "[rb_vector] "
|
|
|
|
typedef struct
|
|
{
|
|
uint32_t id;
|
|
gis_map_projection_t *projection = nullptr;
|
|
gis_view_scale_t map_scale_m_px;
|
|
gis_view_scale_t inv_map_scale_m_px;
|
|
gis_borders_t canvas_rect;
|
|
gis_borders_t draw_canvas_border;
|
|
gis_helper_math_ctx_t math_ctx;
|
|
gis_mdp_t mdp = nullptr;
|
|
uint32_t view_scale_phys;
|
|
double_point_t surface_ul_meters;
|
|
QPainter *painter = nullptr;
|
|
|
|
} render_task_info_t;
|
|
|
|
static int map_generator_internal_render( rb_desc_t *rb_ctx );
|
|
static void render_task( render_task_info_t *rinfo, gis_databuffer_desc_object_t *descObject );
|
|
|
|
int rb_map_render( rb_desc_t *rb_ctx )
|
|
{
|
|
time_stat_t full_render_timer;
|
|
time_stat_init( &full_render_timer );
|
|
time_stat_set_enabled( &full_render_timer );
|
|
|
|
time_stat_start( &full_render_timer );
|
|
|
|
int res = map_generator_internal_render( rb_ctx );
|
|
|
|
time_stat_sample( &full_render_timer );
|
|
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_INFO, RB_VECTOR_PREFIX,
|
|
"Render finished / status: %d / time: %.3f s [%s()]",
|
|
res, time_stat_get_full_seconds( &full_render_timer ), __FUNCTION__ );
|
|
|
|
return res;
|
|
}
|
|
|
|
static bool conversion_flag_log = false;
|
|
|
|
static int map_generator_internal_render( rb_desc_t *rb_ctx )
|
|
{
|
|
if ( rb_ctx == NULL )
|
|
{
|
|
return EINVAL;
|
|
}
|
|
|
|
int status = EOK;
|
|
|
|
gis_mdp_t mdp = gis_data_engine_get_display_parameters( rb_ctx->data_engine_ctx );
|
|
gis_view_scale_t map_scale_m_px;
|
|
|
|
gis_mdp_get_scale_m_px( mdp, &map_scale_m_px );
|
|
|
|
double_point_t region_ul_meters;
|
|
double_point_t region_lr_meters;
|
|
|
|
int32_point_t draw_canvas_size_px = { rb_ctx->viewport_size.x, rb_ctx->viewport_size.y };
|
|
gis_borders_t draw_canvas_border;
|
|
double_point_t surface_ul_meters;
|
|
int32_point_t zero = {0, 0};
|
|
|
|
if ( generators_common_math_get_render_params( mdp,
|
|
zero,
|
|
draw_canvas_size_px,
|
|
&surface_ul_meters,
|
|
®ion_ul_meters,
|
|
®ion_lr_meters,
|
|
&draw_canvas_border ) != EOK )
|
|
{
|
|
return EBADR;
|
|
}
|
|
|
|
gis_borders_t region;
|
|
region.degrees.north = region_ul_meters.y;
|
|
region.degrees.west = region_ul_meters.x;
|
|
region.degrees.south = region_lr_meters.y;
|
|
region.degrees.east = region_lr_meters.x;
|
|
|
|
QRect clip_rect( 0, 0, rb_ctx->viewport_size.x, rb_ctx->viewport_size.y );
|
|
|
|
uint32_t view_scale_phys = gis_mdp_get_phys_scale( mdp );
|
|
|
|
render_task_info_t rinfo;
|
|
rinfo.map_scale_m_px = map_scale_m_px;
|
|
rinfo.canvas_rect = region;
|
|
rinfo.draw_canvas_border = draw_canvas_border;
|
|
rinfo.inv_map_scale_m_px.x = 1.0 / map_scale_m_px.x;
|
|
rinfo.inv_map_scale_m_px.y = 1.0 / map_scale_m_px.y;
|
|
rinfo.view_scale_phys = view_scale_phys;
|
|
|
|
rinfo.projection = gis_mdp_get_projection( mdp );
|
|
rinfo.math_ctx = gis_mdp_get_math_ctx( mdp );
|
|
|
|
rinfo.surface_ul_meters = surface_ul_meters;
|
|
rinfo.mdp = mdp;
|
|
|
|
rinfo.painter = rb_ctx->painter;
|
|
|
|
if ( !rinfo.painter )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_ERROR, RB_VECTOR_PREFIX,
|
|
"Failed to get QPainter [%s()]", __FUNCTION__ );
|
|
|
|
return EFAULT;
|
|
}
|
|
|
|
rinfo.painter->setClipRect( clip_rect );
|
|
|
|
if ( gis_data_raw_assert_maps_projection( rinfo.projection ) != EOK )
|
|
{
|
|
return EFAULT;
|
|
}
|
|
|
|
gis_databuffer_desc_t ddesc = gis_data_engine_get_base_ptr( rb_ctx->data_engine_ctx );
|
|
|
|
if ( ddesc == NULL )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_ERROR, RB_VECTOR_PREFIX,
|
|
"Data descriptor not allocated [%s()]", __FUNCTION__);
|
|
|
|
return ENOBUFS;
|
|
}
|
|
|
|
gis_databuffer_desc_iterator_t diter = gis_databuffer_desc_get_iterator( ddesc );
|
|
|
|
gis_databuffer_desc_set_border_filter( diter, &draw_canvas_border );
|
|
|
|
gis_databuffer_desc_object_t datadesc_object;
|
|
|
|
if ( gis_databuffer_desc_get_first_databuffer_object( diter, &datadesc_object ) != EOK )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_WARNING, RB_VECTOR_PREFIX,
|
|
"No data from draw buffer data descriptor [%s()]", __FUNCTION__);
|
|
|
|
return ENODATA;
|
|
}
|
|
|
|
do
|
|
{
|
|
gcm_object_header_t *pObject = datadesc_object.pObject;
|
|
|
|
if ( pObject == NULL )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_ERROR, RB_VECTOR_PREFIX,
|
|
"Invalid databuffer object ptr [%s()]", __FUNCTION__);
|
|
|
|
break;
|
|
}
|
|
|
|
if ( !gishelper_is_aligned( pObject ) ||
|
|
!gishelper_is_aligned( pObject->point ) ||
|
|
!gishelper_is_aligned( &pObject->br_x ) ||
|
|
!gishelper_is_aligned( &pObject->br_y ) ||
|
|
!gishelper_is_aligned( &pObject->ul_x ) ||
|
|
!gishelper_is_aligned( &pObject->ul_y ) )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_WARNING, RB_VECTOR_PREFIX,
|
|
"Object is not aligned 0x%p / points: 0x%p [%s()]",
|
|
pObject, pObject->point, __FUNCTION__);
|
|
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_INFO, RB_VECTOR_PREFIX,
|
|
"Data buffer object structure size: %d, points offset %d [%s()]",
|
|
sizeof( *pObject ), offsetof( gcm_object_header_t, point ), __FUNCTION__ );
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( ( pObject->mark & DATABUFFER_OBJECT_MARK_MASK ) != DATABUFFER_OBJECT_MARK )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_ERROR, RB_VECTOR_PREFIX,
|
|
"Data buffer mark is broken! 0x%x [%s()]",
|
|
(pObject->mark & DATABUFFER_OBJECT_MARK_MASK), __FUNCTION__);
|
|
|
|
break;
|
|
}
|
|
|
|
if ( pObject->lower_scale_limit != 0 && view_scale_phys < pObject->lower_scale_limit )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( pObject->upper_scale_limit != 0 && view_scale_phys > pObject->upper_scale_limit )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( pObject->point_count_all == 0 && pObject->point_count_main == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
render_task( &rinfo, &datadesc_object );
|
|
|
|
} while ( gis_databuffer_desc_get_next_databuffer_object( diter, &datadesc_object ) == EOK );
|
|
|
|
|
|
return status;
|
|
}
|
|
|
|
static int get_attributes( gcm_object_header_t *pObject, std::vector<RenderAttribute> &v_attributes )
|
|
{
|
|
gcm_map_header_t *pMap = gcm_object_get_map_header( pObject );
|
|
|
|
if ( !pMap )
|
|
{
|
|
return EBADF;
|
|
}
|
|
|
|
if ( pMap->data_source != GIS_CORE_MAP_DATA_SOURCE_S57 )
|
|
{
|
|
return ENODATA;
|
|
}
|
|
|
|
gcm_attribute_header_t *pAttr = (gcm_attribute_header_t *)( (char*)pObject + pObject->attribute_offset );
|
|
|
|
if ( !gishelper_is_aligned( pAttr ) )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_WARNING, RB_VECTOR_PREFIX,
|
|
"Attribute is not aligned 0x%p [%s()]", pAttr, __FUNCTION__ );
|
|
}
|
|
|
|
for ( uint32_t iAttr = 0; iAttr < pObject->attribute_cnt; ++iAttr )
|
|
{
|
|
size_t data_len = pAttr->entry_length - sizeof( gcm_attribute_header_t );
|
|
size_t attr_len = 0;
|
|
|
|
switch ( pAttr->type_idx )
|
|
{
|
|
case GCM_ATTR_TYPE_INTEGER:
|
|
{
|
|
attr_len = data_len * 2 + 2;
|
|
break;
|
|
}
|
|
case GCM_ATTR_TYPE_DOUBLE:
|
|
{
|
|
attr_len = data_len * 2 + 1;
|
|
break;
|
|
}
|
|
case GCM_ATTR_TYPE_TEXT:
|
|
{
|
|
attr_len = data_len + 1;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
attr_len = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
char attr_value[attr_len];
|
|
gcm_attribute_get_value_as_string_s( pAttr, attr_len, attr_value );
|
|
|
|
RenderAttribute attr( pAttr->idx, pAttr->type_idx, attr_value );
|
|
v_attributes.push_back( attr );
|
|
|
|
pAttr = (gcm_attribute_header_t*)( (char*)pAttr + pAttr->entry_length );
|
|
}
|
|
|
|
return EOK;
|
|
}
|
|
|
|
static int assert_points_conversion( gcm_object_header_t *pObject,
|
|
gis_helper_math_ctx_t math_ctx )
|
|
{
|
|
if ( !pObject->is_object_reprojected )
|
|
{
|
|
if ( !conversion_flag_log )
|
|
{
|
|
conversion_flag_log = true;
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_INFO, RB_VECTOR_PREFIX,
|
|
"At least one object points conversion enabled [%s()]", __FUNCTION__);
|
|
}
|
|
|
|
return gcm_object_assert_meter_points(pObject, math_ctx);
|
|
}
|
|
|
|
return EOK;
|
|
}
|
|
|
|
static void render_task( render_task_info_t *r_info, gis_databuffer_desc_object_t *descObject )
|
|
{
|
|
gcm_object_header_t *pObject = descObject->pObject;
|
|
gis_view_scale_t inv_map_scale_m_px = r_info->inv_map_scale_m_px;
|
|
gis_view_scale_t map_scale_m_px = r_info->map_scale_m_px;
|
|
gis_helper_math_ctx_t math_ctx = r_info->math_ctx;
|
|
QPainter *painter = r_info->painter;
|
|
|
|
if ( gis_mdp_get_map_antialiasing_level( r_info->mdp ) )
|
|
{
|
|
painter->setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing);
|
|
}
|
|
|
|
if ( assert_points_conversion( pObject, math_ctx ) != EOK )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( gcm_object_assert_bounding_rect( pObject ) != EOK )
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool render_required = false;
|
|
|
|
double_point_t border_ul = { pObject->ul_x, pObject->ul_y };
|
|
double_point_t border_br = { pObject->br_x, pObject->br_y };
|
|
double_point_t border_deg_ul;
|
|
double_point_t border_deg_br;
|
|
|
|
gis_helper_convert_point_meters_2_degrees( math_ctx, &border_deg_ul, &border_ul );
|
|
gis_helper_convert_point_meters_2_degrees( math_ctx, &border_deg_br, &border_br );
|
|
|
|
gis_borders_t object_degrees_border;
|
|
object_degrees_border.degrees.north = border_deg_ul.y;
|
|
object_degrees_border.degrees.west = border_deg_ul.x;
|
|
object_degrees_border.degrees.south = border_deg_br.y;
|
|
object_degrees_border.degrees.east = border_deg_br.x;
|
|
|
|
render_required = generators_common_math_is_render_required( static_cast<gis_object_primitive_type_t>(pObject->type),
|
|
&object_degrees_border,
|
|
&r_info->draw_canvas_border );
|
|
|
|
if ( pObject->type == GIS_OBJECT_PRIMITIVE_POLYGON )
|
|
{
|
|
double obj_w = pObject->br_x - pObject->ul_x;
|
|
double obj_h = pObject->ul_y - pObject->br_y;
|
|
|
|
if ( ( fabs( obj_h * inv_map_scale_m_px.y ) < 1 ) ||
|
|
( fabs( obj_w * inv_map_scale_m_px.x ) < 1 ) )
|
|
{
|
|
render_required = false;
|
|
}
|
|
}
|
|
|
|
if ( !render_required )
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool isClippingEnabled = false;
|
|
|
|
if ( pObject->type == GIS_OBJECT_PRIMITIVE_POINT ||
|
|
pObject->type == GIS_OBJECT_PRIMITIVE_TEXT ||
|
|
pObject->type == GIS_OBJECT_PRIMITIVE_TEXT_TEMPLATE ||
|
|
pObject->type == GIS_OBJECT_PRIMITIVE_VECTOR )
|
|
{
|
|
isClippingEnabled = false;
|
|
}
|
|
else
|
|
{
|
|
isClippingEnabled = true;
|
|
}
|
|
|
|
painter->setClipping( isClippingEnabled );
|
|
|
|
std::vector<RenderObject> v_objects;
|
|
|
|
RenderObject mainObject( pObject->point_count_main );
|
|
mainObject.text_ptr = pObject->text_length != 0 ? ( (char *)pObject + pObject->text_offset ) : NULL;
|
|
|
|
if ( !gishelper_is_aligned( mainObject.text_ptr ) )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_WARNING, RB_VECTOR_PREFIX,
|
|
"Text is not aligned 0x%p [%s()]", mainObject.text_ptr, __FUNCTION__ );
|
|
}
|
|
|
|
mainObject.text_len = pObject->text_length;
|
|
mainObject.text_alignment = pObject->text_alignment;
|
|
|
|
gcm_object_point_t *pPoint = pObject->point;
|
|
|
|
for ( uint32_t point_idx = 0; point_idx < pObject->point_count_main; ++point_idx, ++pPoint )
|
|
{
|
|
mainObject.pnts[point_idx].setX( (int32_t)( ( pPoint->meters.x - r_info->surface_ul_meters.x ) *
|
|
inv_map_scale_m_px.x) );
|
|
|
|
mainObject.pnts[point_idx].setY( (int32_t)( ( r_info->surface_ul_meters.y - pPoint->meters.y ) *
|
|
inv_map_scale_m_px.y) );
|
|
}
|
|
|
|
v_objects.push_back( mainObject );
|
|
|
|
if ( pObject->subobject_cnt > 0 )
|
|
{
|
|
gcm_subobject_header_t *pSubobject = (gcm_subobject_header_t*)( (char*)pObject + pObject->main_object_sz );
|
|
|
|
for ( uint32_t iSub = 0; iSub < pObject->subobject_cnt; ++iSub )
|
|
{
|
|
RenderObject subObject( pSubobject->point_count );
|
|
|
|
pPoint = pSubobject->point;
|
|
|
|
for ( uint32_t point_idx = 0; point_idx < pSubobject->point_count; ++point_idx, ++pPoint )
|
|
{
|
|
|
|
subObject.pnts[point_idx].setX( (int32_t)( ( pPoint->meters.x - r_info->surface_ul_meters.x ) *
|
|
inv_map_scale_m_px.x ) );
|
|
subObject.pnts[point_idx].setY( (int32_t)( ( r_info->surface_ul_meters.y - pPoint->meters.y ) *
|
|
inv_map_scale_m_px.y) );
|
|
}
|
|
|
|
subObject.text_ptr = pSubobject->text_length != 0 ? ( (char*)pSubobject + pSubobject->text_offset ) : nullptr;
|
|
|
|
if ( !gishelper_is_aligned( subObject.text_ptr ) )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_WARNING, RB_VECTOR_PREFIX,
|
|
"Text is not aligned 0x%p [%s()]", subObject.text_ptr, __FUNCTION__ );
|
|
}
|
|
|
|
subObject.text_len = pSubobject->text_length;
|
|
subObject.text_alignment = pSubobject->text_alignment;
|
|
|
|
v_objects.push_back( subObject );
|
|
|
|
pSubobject = (gcm_subobject_header_t*)( (char*)pSubobject + pSubobject->entry_sz );
|
|
}
|
|
}
|
|
|
|
std::vector<RenderAttribute> v_attributes;
|
|
v_attributes.reserve( pObject->attribute_cnt );
|
|
|
|
get_attributes( pObject, v_attributes );
|
|
|
|
RenderContext *renderCtx = new RenderContext( v_objects, v_attributes );
|
|
|
|
renderCtx->qpainter = painter;
|
|
renderCtx->view_scale = map_scale_m_px;
|
|
renderCtx->view_scale_phys = r_info->view_scale_phys;
|
|
renderCtx->map_scale = descObject->scale_value;
|
|
renderCtx->mdp = r_info->mdp;
|
|
renderCtx->obj_type = (gis_object_primitive_type_t)pObject->type;
|
|
renderCtx->p_class_entry = (UniDatabufferDescriptorClass*)descObject->p_datadesc_obj_grp->p_class_entry;
|
|
renderCtx->data_source = descObject->class_data.src;
|
|
|
|
int result = renderCtx->drawObject();
|
|
|
|
if ( result != EOK )
|
|
{
|
|
if ( result != EALREADY )
|
|
{
|
|
gis_helper_debug_write_lvl( GIS_DEBUG_LEVEL_ERROR, RB_VECTOR_PREFIX,
|
|
"Failed to draw object %d [%s()]", r_info->id, __FUNCTION__ );
|
|
}
|
|
}
|
|
|
|
delete renderCtx;
|
|
}
|