kpda-gis/src/lib/renderbuffer/rb_vector.cpp

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,
&region_ul_meters,
&region_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;
}