klayout/src/laybasic/laybasic/layRedrawThreadWorker.cc

2256 lines
78 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2022 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "layRedrawThreadWorker.h"
#include "layRedrawThread.h"
namespace lay
{
// time delay until the first snapshot is taken
const int first_snapshot_delay = 20;
// -------------------------------------------------------------
static inline db::Box safe_transformed_box (const db::Box &box, const db::ICplxTrans &t)
{
db::DBox db = db::CplxTrans (t) * box;
db &= db::DBox (db::Box::world ());
return db::Box (db);
}
// -------------------------------------------------------------
// RedrawThreadWorker implementation
RedrawThreadWorker::RedrawThreadWorker (RedrawThread *redraw_thread)
: mp_redraw_thread (redraw_thread)
{
mp_layout = 0;
mp_cell_var_cache = 0;
m_cache_hits = 0;
m_cache_misses = 0;
m_cv_index = -1;
mp_canvas = 0;
m_test_count = 0;
m_from_level = 0;
m_to_level = 0;
m_from_level_default = 0;
m_to_level_default = 0;
m_nlayers = 0;
m_box_text_transform = false;
m_box_font = 0;
m_min_size_for_label = 1;
m_text_font = 0;
m_text_visible = false;
m_text_lazy_rendering = false;
m_bitmap_caching = false;
m_show_properties = false;
m_apply_text_trans = false;
m_default_text_size = 0.0;
m_drop_small_cells = false;
m_drop_small_cells_value = 0;
m_drop_small_cells_cond = lay::LayoutView::DSC_Min;
m_draw_array_border_instances = false;
m_abstract_mode_width = 0;
m_child_context_enabled = false;
m_layer = 0;
m_xfill = false;
mp_prop_sel = 0;
m_inv_prop_sel = false;
m_clock = tl::Clock::current ();
for (unsigned int i = 0; i < sizeof (m_planes) / sizeof (m_planes[0]); ++i) {
m_planes[i] = 0;
}
}
RedrawThreadWorker::~RedrawThreadWorker ()
{
for (unsigned int i = 0; i < sizeof (m_planes) / sizeof (m_planes[0]); ++i) {
if (m_planes[i]) {
delete m_planes[i];
m_planes[i] = 0;
}
}
}
void
RedrawThreadWorker::perform_task (tl::Task *task)
{
RedrawThreadTask *redraw_thread_task = dynamic_cast <RedrawThreadTask *> (task);
if (! redraw_thread_task) {
return;
}
m_cell_cache.clear ();
m_mi_cache.clear ();
m_mi_text_cache.clear ();
m_from_level = m_from_level_default;
m_to_level = m_to_level_default;
int task_id = redraw_thread_task->id ();
if (task_id >= 0) {
// draw a layer
// HINT: the order in which the planes are delivered (the index stored in the first member of the pair below)
// must correspond with the order by which the ViewOp's are created inside LayoutView::set_view_ops
m_buffers.clear ();
for (unsigned int i = 0; i < (unsigned int) planes_per_layer / 3; ++i) {
// context level planes
unsigned int i1 = task_id * (planes_per_layer / 3) + special_planes_before + i;
mp_canvas->initialize_plane (m_planes[i], i1);
m_buffers.push_back (std::make_pair (i1, m_planes [i]));
// child level planes (if used)
unsigned int i2 = (task_id + m_nlayers) * (planes_per_layer / 3) + special_planes_before + i;
mp_canvas->initialize_plane (m_planes [i + planes_per_layer / 3], i2);
m_buffers.push_back (std::make_pair (i2, m_planes [i + planes_per_layer / 3]));
// current level planes
unsigned int i3 = (task_id + m_nlayers * 2) * (planes_per_layer / 3) + special_planes_before + i;
mp_canvas->initialize_plane (m_planes [i + 2 * (planes_per_layer / 3)], i3);
m_buffers.push_back (std::make_pair (i3, m_planes [i + 2 * (planes_per_layer / 3)]));
}
// detect whether the text planes are empty. If not, the whole text plane must be redrawn to account for clipped texts
bool text_planes_empty = true;
for (unsigned int i = 0; i < (unsigned int) planes_per_layer && text_planes_empty; i += (unsigned int) planes_per_layer / 3) {
lay::Bitmap *text = dynamic_cast<lay::Bitmap *> (m_planes[i + 2]);
if (text && ! text->empty ()) {
text_planes_empty = false;
}
}
std::vector<db::Box> text_redraw_regions = m_redraw_region;
if (! text_planes_empty) {
// if there are non-empty text planes, redraw the whole area for texts
text_redraw_regions.clear ();
text_redraw_regions.push_back(db::Box(0, 0, mp_canvas->canvas_width (), mp_canvas->canvas_height ()));
for (unsigned int i = 0; i < (unsigned int) planes_per_layer; i += (unsigned int) planes_per_layer / 3) {
lay::Bitmap *text = dynamic_cast<lay::Bitmap *> (m_planes[i + 2]);
if (text) {
text->clear ();
}
}
}
const RedrawLayerInfo &li = mp_redraw_thread->get_layer_info (task_id);
if (li.cellview_index >= 0) {
// determine layout and cell associated with this layer ..
const lay::CellView &cv = m_cellviews [li.cellview_index];
if (cv.is_valid () && ! cv->layout ().under_construction () && ! (cv->layout ().manager () && cv->layout ().manager ()->transacting ())) {
mp_layout = &cv->layout ();
m_cv_index = li.cellview_index;
db::cell_index_type ci = cv.cell_index ();
int ctx_path_length = int (m_cellviews [m_cv_index].specific_path ().size ());
if (li.hier_levels.has_from_level ()) {
m_from_level = li.hier_levels.from_level (ctx_path_length, m_from_level);
}
if (li.hier_levels.has_to_level ()) {
m_to_level = li.hier_levels.to_level (ctx_path_length, m_to_level);
}
m_xfill = li.xfill;
mp_prop_sel = &li.prop_sel;
m_inv_prop_sel = li.inverse_prop_sel;
if (mp_prop_sel->empty () && m_inv_prop_sel) {
// no property selection
mp_prop_sel = 0;
}
if (li.layer_index >= 0) {
m_layer = li.layer_index;
if (tl::verbosity () >= 40) {
tl::info << tl::to_string (QObject::tr ("Drawing layer: ")) << mp_layout->get_properties (m_layer).name;
}
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (QObject::tr ("Drawing layer")));
// configure renderer ..
mp_renderer->set_xfill (m_xfill);
mp_renderer->draw_texts (m_text_visible);
mp_renderer->draw_properties (m_show_properties);
mp_renderer->draw_description_property (false);
mp_renderer->default_text_size (db::Coord (m_default_text_size / mp_layout->dbu ()));
mp_renderer->set_font (db::Font (m_text_font));
mp_renderer->apply_text_trans (m_apply_text_trans);
for (std::vector<db::DCplxTrans>::const_iterator t = li.trans.begin (); t != li.trans.end (); ++t) {
db::CplxTrans trans = m_vp_trans * *t * db::CplxTrans (mp_layout->dbu ());
iterate_variants (m_redraw_region, ci, trans, &RedrawThreadWorker::draw_layer);
iterate_variants (text_redraw_regions, ci, trans, &RedrawThreadWorker::draw_text_layer);
}
} else if (li.cell_frame) {
// no xfill for cell boxes
mp_renderer->set_xfill (false);
// if no specific layer is assigned, draw cell boxes with the style given
if (tl::verbosity () >= 40) {
tl::info << tl::to_string (QObject::tr ("Drawing custom frames"));
}
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (QObject::tr ("Drawing frames")));
for (std::set< std::pair<db::DCplxTrans, int> >::const_iterator b = m_box_variants.begin (); b != m_box_variants.end (); ++b) {
if (b->second == li.cellview_index) {
db::CplxTrans trans = m_vp_trans * b->first * db::CplxTrans (mp_layout->dbu ());
iterate_variants (m_redraw_region, ci, trans, &RedrawThreadWorker::draw_boxes);
iterate_variants (text_redraw_regions, ci, trans, &RedrawThreadWorker::draw_box_properties);
}
}
}
mp_prop_sel = 0;
m_inv_prop_sel = false;
}
}
} else if (task_id == draw_boxes_queue_entry) {
// draw the bounding boxes
if (tl::verbosity () >= 40) {
tl::info << tl::to_string (QObject::tr ("Drawing frames and guiding shapes"));
}
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (QObject::tr ("Drawing frames and guiding shapes")));
// No xfill for cell boxes
mp_renderer->set_xfill (false);
// HINT: the order in which the planes are delivered (the index stored in the first member of the pair below)
// must correspond with the order by which the ViewOp's are created inside LayoutView::set_view_ops
m_buffers.clear ();
for (unsigned int i = 0; i < (unsigned int) planes_per_layer / 3; ++i) {
// context level planes
unsigned int i1 = i;
mp_canvas->initialize_plane (m_planes[i], i1);
m_buffers.push_back (std::make_pair (i1, m_planes [i]));
// child level planes (if used)
unsigned int i2 = i + planes_per_layer / 3;
mp_canvas->initialize_plane (m_planes [i + planes_per_layer / 3], i2);
m_buffers.push_back (std::make_pair (i2, m_planes [i + planes_per_layer / 3]));
// current level planes
unsigned int i3 = i + 2 * (planes_per_layer / 3);
mp_canvas->initialize_plane (m_planes [i + 2 * (planes_per_layer / 3)], i3);
m_buffers.push_back (std::make_pair (i3, m_planes [i + 2 * (planes_per_layer / 3)]));
}
// detect whether the text planes are empty. If not, the whole text plane must be redrawn to account for clipped texts
bool text_planes_empty = true;
for (unsigned int i = 0; i < (unsigned int) planes_per_layer && text_planes_empty; i += (unsigned int) planes_per_layer / 3) {
lay::Bitmap *text = dynamic_cast<lay::Bitmap *> (m_planes[i + 2]);
if (text && ! text->empty ()) {
text_planes_empty = false;
}
}
std::vector<db::Box> text_redraw_regions = m_redraw_region;
if (! text_planes_empty) {
// if there are non-empty text planes, redraw the whole area for texts
text_redraw_regions.clear ();
text_redraw_regions.push_back(db::Box(0, 0, mp_canvas->canvas_width (), mp_canvas->canvas_height ()));
for (unsigned int i = 0; i < (unsigned int) planes_per_layer; i += (unsigned int) planes_per_layer / 3) {
lay::Bitmap *text = dynamic_cast<lay::Bitmap *> (m_planes[i + 2]);
if (text) {
text->clear ();
}
}
}
for (std::set< std::pair<db::DCplxTrans, int> >::const_iterator b = m_box_variants.begin (); b != m_box_variants.end (); ++b) {
const lay::CellView &cv = m_cellviews [b->second];
if (cv.is_valid () && ! cv->layout ().under_construction () && ! (cv->layout ().manager () && cv->layout ().manager ()->transacting ())) {
mp_layout = &cv->layout ();
m_cv_index = b->second;
db::CplxTrans trans = m_vp_trans * b->first * db::CplxTrans (mp_layout->dbu ());
iterate_variants (m_redraw_region, cv.cell_index (), trans, &RedrawThreadWorker::draw_boxes);
iterate_variants (text_redraw_regions, cv.cell_index (), trans, &RedrawThreadWorker::draw_box_properties);
}
}
transfer ();
// HINT: the order in which the planes are delivered (the index stored in the first member of the pair below)
// must correspond with the order by which the ViewOp's are created inside LayoutView::set_view_ops
m_buffers.clear ();
for (unsigned int i = 0; i < (unsigned int) planes_per_layer / 3; ++i) {
// context level planes
unsigned int i1 = cell_box_planes + i;
mp_canvas->initialize_plane (m_planes[i], i1);
m_buffers.push_back (std::make_pair (i1, m_planes [i]));
// child level planes (if used)
unsigned int i2 = cell_box_planes + i + planes_per_layer / 3;
mp_canvas->initialize_plane (m_planes [i + planes_per_layer / 3], i2);
m_buffers.push_back (std::make_pair (i2, m_planes [i + planes_per_layer / 3]));
// current level planes
unsigned int i3 = cell_box_planes + i + 2 * (planes_per_layer / 3);
mp_canvas->initialize_plane (m_planes [i + 2 * (planes_per_layer / 3)], i3);
m_buffers.push_back (std::make_pair (i3, m_planes [i + 2 * (planes_per_layer / 3)]));
}
// detect whether the text planes are empty. If not, the whole text plane must be redrawn to account for clipped texts
text_planes_empty = true;
for (unsigned int i = 0; i < (unsigned int) planes_per_layer && text_planes_empty; i += (unsigned int) planes_per_layer / 3) {
lay::Bitmap *text = dynamic_cast<lay::Bitmap *> (m_planes[i + 2]);
if (text && ! text->empty ()) {
text_planes_empty = false;
}
}
text_redraw_regions = m_redraw_region;
if (! text_planes_empty) {
// if there are non-empty text planes, redraw the whole area for texts
text_redraw_regions.clear ();
text_redraw_regions.push_back(db::Box(0, 0, mp_canvas->canvas_width (), mp_canvas->canvas_height ()));
for (unsigned int i = 0; i < (unsigned int) planes_per_layer; i += (unsigned int) planes_per_layer / 3) {
lay::Bitmap *text = dynamic_cast<lay::Bitmap *> (m_planes[i + 2]);
if (text) {
text->clear ();
}
}
}
// draw the guiding and error shapes
for (std::set< std::pair<db::DCplxTrans, int> >::const_iterator b = m_box_variants.begin (); b != m_box_variants.end (); ++b) {
const lay::CellView &cv = m_cellviews [b->second];
if (cv.is_valid () && ! cv->layout ().under_construction () && ! (cv->layout ().manager () && cv->layout ().manager ()->transacting ())) {
mp_layout = &cv->layout ();
m_cv_index = b->second;
db::CplxTrans trans = m_vp_trans * b->first * db::CplxTrans (mp_layout->dbu ());
mp_prop_sel = 0;
m_inv_prop_sel = false;
// draw one level more to show the guiding shapes as part of the instance
m_to_level += 1; // TODO: modifying this basic setting is a hack!
// configure renderer ..
mp_renderer->draw_texts (m_text_visible);
mp_renderer->draw_properties (false);
mp_renderer->draw_description_property (true);
mp_renderer->default_text_size (db::Coord (m_default_text_size / mp_layout->dbu ()));
mp_renderer->set_font (db::Font (m_text_font));
mp_renderer->apply_text_trans (m_apply_text_trans);
bool f = m_text_lazy_rendering;
try {
m_text_lazy_rendering = false;
m_layer = mp_layout->guiding_shape_layer ();
iterate_variants (m_redraw_region, cv.cell_index (), trans, &RedrawThreadWorker::draw_layer);
iterate_variants (text_redraw_regions, cv.cell_index (), trans, &RedrawThreadWorker::draw_text_layer);
m_layer = mp_layout->error_layer ();
iterate_variants (m_redraw_region, cv.cell_index (), trans, &RedrawThreadWorker::draw_layer);
iterate_variants (text_redraw_regions, cv.cell_index (), trans, &RedrawThreadWorker::draw_text_layer);
m_text_lazy_rendering = f;
m_to_level -= 1;
} catch (...) {
m_text_lazy_rendering = f;
m_to_level -= 1;
throw;
}
}
}
} else if (task_id == draw_custom_queue_entry) {
// draw the decorations
if (tl::verbosity () >= 40) {
tl::info << tl::to_string (QObject::tr ("Drawing decorations"));
}
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (QObject::tr ("Drawing decorations")));
m_buffers.clear ();
mp_canvas->initialize_plane (m_planes[0], m_nlayers * planes_per_layer + special_planes_before);
m_buffers.push_back (std::make_pair (m_nlayers * planes_per_layer + special_planes_before, m_planes [0]));
unsigned int nd = 0;
for (std::vector <lay::Drawing *>::iterator d = mp_drawings.begin (); d != mp_drawings.end (); ++d, ++nd) {
// temporarily create bitmap objects, paint on them,
// transfer them to the canvas and clear them again.
// This operation is not interrupted by any "test_snapshot".
std::vector <lay::CanvasPlane *> tmp_planes;
tmp_planes.reserve ((*d)->num_planes ());
for (unsigned int i = 0; i < (*d)->num_planes (); ++i) {
tmp_planes.push_back (mp_canvas->create_drawing_plane ());
mp_canvas->initialize_plane (tmp_planes.back (), nd, i);
}
// currently, all cellviews are painted over each other ..
for (unsigned int i = 0; i < m_cellviews.size (); ++i) {
test_snapshot (0);
const lay::CellView &cv = m_cellviews [i];
if (cv.is_valid () && ! cv->layout ().under_construction () && ! (cv->layout ().manager () && cv->layout ().manager ()->transacting ())) {
db::CplxTrans trans (m_vp_trans * cv->layout ().dbu ());
(*d)->paint_cv_on_planes (cv, trans, tmp_planes);
}
}
// do the non-cv-related painting
test_snapshot (0);
(*d)->paint_on_planes (m_vp_trans, tmp_planes, *mp_renderer);
for (unsigned int i = 0; i < (*d)->num_planes (); ++i) {
mp_canvas->set_drawing_plane (nd, i, tmp_planes [i]);
}
while (! tmp_planes.empty ()) {
delete tmp_planes.back ();
tmp_planes.pop_back ();
}
}
}
transfer ();
m_buffers.clear ();
if (tl::verbosity () >= 30) {
for (cell_cache_t::iterator cc = m_cell_cache.begin(); cc != m_cell_cache.end (); ++cc) {
tl::info << "Cell cache: " << mp_layout->cell_name(cc->first.ci) << " (" << cc->first.nlevels << ":" << cc->first.trans.to_string() << ") "
<< cc->second.fill->width() << " x " << cc->second.fill->height() << " -> " << cc->second.hits << " hits";
}
}
m_cell_cache.clear ();
mp_redraw_thread->task_finished (task_id);
}
void
RedrawThreadWorker::finish ()
{
// release all cellview references here.
m_cellviews.clear ();
// free the planes
for (unsigned int i = 0; i < sizeof (m_planes) / sizeof (m_planes[0]); ++i) {
if (m_planes[i]) {
delete m_planes[i];
m_planes[i] = 0;
}
}
}
void
RedrawThreadWorker::setup (LayoutView *view, RedrawThreadCanvas *canvas, const std::vector<db::Box> &redraw_region, const db::DCplxTrans &vp_trans)
{
m_redraw_region = redraw_region;
m_vp_trans = vp_trans;
mp_canvas = canvas;
mp_drawings.clear ();
for (lay::Drawings::iterator d = view->drawings ()->begin (); d != view->drawings ()->end (); ++d) {
mp_drawings.push_back (&*d);
}
// allow a very short time to pass before we issue the
// first update event.
m_clock = tl::Clock::current () - tl::Clock ((update_interval - first_snapshot_delay) * 0.001);
// initialize the drawing planes
for (unsigned int i = 0; i < (unsigned int) planes_per_layer; ++i) {
if (m_planes[i]) {
delete m_planes[i];
}
m_planes[i] = mp_canvas->create_drawing_plane ();
}
mp_renderer.reset (mp_canvas->create_renderer ());
// copy everything that we need so there is no need to access
// members of lay::LayoutView in the drawing thread.
// Note: copying the cellviews will create new references to the
// layout objects. These are not automatically freed when the
// drawing ends but rather on "stop". The advantage of this is
// that, since "stop" is called from the main thread like "start",
// we don't challenge lay::CellView's MT compliance.
m_from_level_default = view->get_hier_levels ().first;
m_to_level_default = view->get_hier_levels ().second;
m_min_size_for_label = view->min_inst_label_size ();
m_box_text_transform = view->cell_box_text_transform ();
m_box_font = view->cell_box_text_font ();
m_text_font = view->text_font ();
m_text_visible = view->text_visible ();
m_text_lazy_rendering = view->text_lazy_rendering ();
m_bitmap_caching = view->bitmap_caching ();
m_show_properties = view->show_properties_as_text ();
m_apply_text_trans = view->apply_text_trans ();
m_default_text_size = view->default_text_size ();
m_drop_small_cells = view->drop_small_cells ();
m_drop_small_cells_value = view->drop_small_cells_value ();
m_drop_small_cells_cond = view->drop_small_cells_cond ();
m_draw_array_border_instances = view->draw_array_border_instances ();
m_abstract_mode_width = view->abstract_mode_width ();
m_child_context_enabled = view->child_context_enabled ();
m_test_count = 0;
mp_prop_sel = 0;
m_inv_prop_sel = false;
m_hidden_cells = view->hidden_cells ();
m_cellviews.clear ();
m_cellviews.reserve (view->cellviews ());
for (unsigned int i = 0; i < view->cellviews (); ++i) {
m_cellviews.push_back (view->cellview (i));
}
m_nlayers = mp_redraw_thread->num_layers ();
m_box_variants = view->cv_transform_variants ();
}
void
RedrawThreadWorker::transfer ()
{
for (std::vector<std::pair<unsigned int, lay::CanvasPlane *> >::iterator b = m_buffers.begin (); b != m_buffers.end (); ++b) {
mp_canvas->set_plane (b->first, b->second);
}
}
void
RedrawThreadWorker::test_snapshot (const UpdateSnapshotCallback *update_snapshot)
{
checkpoint ();
if (mp_redraw_thread->num_workers () > 0) {
if (m_test_count == 0) {
m_test_count = 100; // TODO: make variable?
tl::Clock c = tl::Clock::current ();
if ((c - m_clock).seconds () > update_interval * 0.001) {
if (update_snapshot) {
update_snapshot->trigger ();
}
transfer ();
mp_redraw_thread->wakeup_checked ();
m_clock = c;
}
} else {
--m_test_count;
}
}
}
void
RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, const std::string &txt)
{
lay::Renderer &r = *mp_renderer;
unsigned int plane_group = 2;
if (drawing_context) {
plane_group = 0;
} else if (m_child_context_enabled && level > 0) {
plane_group = 1;
}
db::DBox dbox = trans * box;
lay::CanvasPlane *fill = m_planes[0 + plane_group * (planes_per_layer / 3)];
lay::CanvasPlane *contour = m_planes[1 + plane_group * (planes_per_layer / 3)];
r.draw (box, trans, fill, contour, 0, 0);
if (! txt.empty () && dbox.width () > m_min_size_for_label && dbox.height () > m_min_size_for_label) {
// Hint: we render to contour because the texts plane is reserved for properties
r.draw (dbox, txt,
db::Font (m_box_font),
db::HAlignCenter,
db::VAlignCenter,
// TODO: apply "real" transformation?
db::DFTrans (m_box_text_transform ? trans.fp_trans ().rot () : db::DFTrans::r0), 0, 0, 0, contour);
}
}
void
RedrawThreadWorker::draw_cell_properties (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, db::properties_id_type prop_id)
{
if (prop_id == 0 || ! m_show_properties) {
return;
}
lay::Renderer &r = *mp_renderer;
unsigned int plane_group = 2;
if (drawing_context) {
plane_group = 0;
} else if (m_child_context_enabled && level > 0) {
plane_group = 1;
}
lay::CanvasPlane *texts = m_planes[2 + plane_group * (planes_per_layer / 3)];
r.draw_propstring (prop_id, &mp_layout->properties_repository (), (trans * box).p1 (), texts, trans);
}
static bool
cells_in (const db::Layout *layout, const db::Cell &cell,
const std::set <db::cell_index_type> &selected,
int levels,
std::set <std::pair <int, db::cell_index_type> > &cache)
{
if (selected.find (cell.cell_index ()) != selected.end ()) {
return true;
}
if (levels > 0) {
for (db::Cell::child_cell_iterator c = cell.begin_child_cells (); ! c.at_end (); ++c) {
if (cache.find (std::make_pair (levels, *c)) == cache.end ()) {
if (cells_in (layout, layout->cell (*c), selected, levels - 1, cache)) {
return true;
}
cache.insert (std::make_pair (levels, *c));
}
}
}
return false;
}
static bool
need_draw_box (const db::Layout *layout, const db::Cell &cell,
int level, int to_level,
const std::vector <std::set <db::cell_index_type> > &hidden_cells, unsigned int cv_index)
{
if (level > to_level) {
return false;
}
if (hidden_cells.size () > cv_index && ! hidden_cells [cv_index].empty ()) {
std::set <std::pair <int, db::cell_index_type> > cache;
if (cells_in (layout, cell, hidden_cells [cv_index], to_level - level, cache)) {
return true;
}
}
return int (cell.hierarchy_levels ()) + level >= to_level;
}
void
RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &redraw_regions, int level)
{
// do not draw, if there is nothing to draw
if (mp_layout->cells () <= ci || redraw_regions.empty ()) {
return;
}
const db::Cell &cell = mp_layout->cell (ci);
// we will never come to a valid level ..
if (! need_draw_box (mp_layout, cell, level, m_to_level, m_hidden_cells, m_cv_index)) {
return;
}
if (cell_var_cached (ci, trans)) {
return;
}
for (std::vector<db::Box>::const_iterator b = redraw_regions.begin (); b != redraw_regions.end (); ++b) {
draw_boxes (drawing_context, ci, trans, *b, level);
}
}
void
RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level)
{
lay::Renderer &r = *mp_renderer;
const db::Cell &cell = mp_layout->cell (ci);
// For small bboxes, the cell outline can be reduced ..
db::Box bbox = cell.bbox ();
if (bbox.empty ()) {
// no shapes there and below ..
} else if (m_drop_small_cells && drop_cell (cell, trans)) {
// small cell dropped
} else if (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())) {
// paint the box on this level
draw_cell (drawing_context, level, trans, bbox, mp_layout->display_name (ci));
} else if (level < m_to_level) {
db::DBox dbbox = trans * bbox;
if (dbbox.width () < 1.5 && dbbox.height () < 1.5) {
if (need_draw_box (mp_layout, cell, level, m_to_level, m_hidden_cells, m_cv_index)) {
// the cell is a very small box and we know there must be
// some level at which to draw the boundary: just draw it
// here and stop looking further down ..
draw_cell (drawing_context, level, trans, bbox, std::string ());
}
} else {
db::box_convert <db::CellInst> bc (*mp_layout);
// create a set of boxes to look into
db::Coord aw = db::coord_traits<db::Coord>::rounded (m_abstract_mode_width / mp_layout->dbu ());
std::vector<db::Box> vv;
if (level == 1 && m_abstract_mode_width > 0 && bbox.width () > db::Box::distance_type (aw * 2) && bbox.height () > db::Box::distance_type (aw * 2)) {
vv.reserve (4);
vv.push_back (redraw_box & db::Box (bbox.left (), bbox.bottom (), bbox.left () + aw, bbox.top ()));
vv.push_back (redraw_box & db::Box (bbox.right () - aw, bbox.bottom (), bbox.right (), bbox.top ()));
vv.push_back (redraw_box & db::Box (bbox.left () + aw, bbox.bottom (), bbox.right () - aw, bbox.bottom () + aw));
vv.push_back (redraw_box & db::Box (bbox.left () + aw, bbox.top () - aw, bbox.right () - aw, bbox.top ()));
} else {
vv.reserve (1);
vv.push_back (redraw_box);
}
// dive down into the hierarchy ..
for (std::vector<db::Box>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
if (! v->empty ()) {
bool anything = false;
db::cell_index_type last_ci = std::numeric_limits<db::cell_index_type>::max ();
db::Cell::touching_iterator inst = cell.begin_touching (*v);
while (! inst.at_end ()) {
const db::CellInstArray &cell_inst = inst->cell_inst ();
db::cell_index_type new_ci = cell_inst.object ().cell_index ();
db::Box new_cell_box = mp_layout->cell (new_ci).bbox ();
if (last_ci != new_ci) {
// Hint: don't use any_cell_box on partially visible cells because that will degrade performance
if (new_cell_box.inside (*v)) {
last_ci = new_ci;
anything = any_cell_box (new_ci, m_to_level - (level + 1));
} else {
anything = true;
}
}
if (anything) {
db::Vector a, b;
unsigned long amax, bmax;
bool simplify = false;
if (cell_inst.is_regular_array (a, b, amax, bmax)) {
db::DBox inst_box;
if (cell_inst.is_complex ()) {
inst_box = trans * (cell_inst.complex_trans () * new_cell_box);
} else {
inst_box = trans * new_cell_box;
}
if (((a.x () == 0 && b.y () == 0) || (a.y () == 0 && b.x () == 0)) &&
inst_box.width () < 1.5 && inst_box.height () < 1.5 &&
(amax <= 1 || trans.ctrans (a.length ()) < 1.5) &&
(bmax <= 1 || trans.ctrans (b.length ()) < 1.5)) {
simplify = true;
}
}
if (simplify) {
// The array can be simplified if there are levels below to draw
if (need_draw_box (mp_layout, mp_layout->cell (new_ci), level + 1, m_to_level, m_hidden_cells, m_cv_index)) {
db::box_convert <db::CellInst> bc (*mp_layout);
unsigned int plane_group = 2;
if (drawing_context) {
plane_group = 0;
} else if (m_child_context_enabled && level + 1 > 0) {
plane_group = 1;
}
lay::CanvasPlane *contour = m_planes[1 + plane_group * (planes_per_layer / 3)];
r.draw (cell_inst.bbox (bc), trans, contour, 0, 0, 0);
}
} else {
size_t qid = 0;
// The array (or single instance) must be iterated instance by instance
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) {
test_snapshot (0);
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = safe_transformed_box (*v, t.inverted ());
draw_boxes (drawing_context, new_ci, trans * t, new_vp, level + 1);
if (p.quad_id () > 0 && p.quad_id () != qid) {
qid = p.quad_id ();
// if the quad is very small we don't gain anything from looking further into the quad - skip this one
db::DBox qb = trans * cell_inst.quad_box (p, bc);
if (qb.width () < 1.0 && qb.height () < 1.0) {
p.skip_quad ();
continue;
}
}
++p;
}
}
}
++inst;
}
}
}
}
}
}
void
RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vp, int level)
{
if (! m_text_visible) {
return;
}
draw_box_properties (drawing_context, ci, trans, vp, level, 0);
}
void
RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vp, int level, db::properties_id_type prop_id)
{
// do not draw, if there is nothing to draw
if (mp_layout->cells () <= ci || vp.empty ()) {
return;
}
const db::Cell &cell = mp_layout->cell (ci);
// we will never come to a valid level ..
if (! need_draw_box (mp_layout, cell, level, m_to_level, m_hidden_cells, m_cv_index)) {
return;
}
if (cell_var_cached (ci, trans)) {
return;
}
for (std::vector<db::Box>::const_iterator b = vp.begin (); b != vp.end (); ++b) {
draw_box_properties (drawing_context, ci, trans, *b, level, prop_id);
}
}
void
RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, db::properties_id_type prop_id)
{
const db::Cell &cell = mp_layout->cell (ci);
// For small bboxes, the cell outline can be reduced ..
db::Box bbox = cell.bbox ();
if (bbox.empty ()) {
// no shapes there and below ..
} else if (m_drop_small_cells && drop_cell (cell, trans)) {
// small cell dropped
} else if (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())) {
// paint the box on this level
draw_cell_properties (drawing_context, level, trans, bbox, prop_id);
} else if (level < m_to_level) {
db::DBox dbbox = trans * bbox;
if (dbbox.width () < 1.5 && dbbox.height () < 1.5) {
// ignore cells which are small
} else {
db::box_convert <db::CellInst> bc (*mp_layout);
// create a set of boxes to look into
db::Coord aw = db::coord_traits<db::Coord>::rounded (m_abstract_mode_width / mp_layout->dbu ());
std::vector<db::Box> vv;
if (level == 1 && m_abstract_mode_width > 0 && bbox.width () > db::Box::distance_type (aw * 2) && bbox.height () > db::Box::distance_type (aw * 2)) {
vv.reserve (4);
vv.push_back (vp & db::Box (bbox.left (), bbox.bottom (), bbox.left () + aw, bbox.top ()));
vv.push_back (vp & db::Box (bbox.right () - aw, bbox.bottom (), bbox.right (), bbox.top ()));
vv.push_back (vp & db::Box (bbox.left () + aw, bbox.bottom (), bbox.right () - aw, bbox.bottom () + aw));
vv.push_back (vp & db::Box (bbox.left () + aw, bbox.top () - aw, bbox.right () - aw, bbox.top ()));
} else {
vv.reserve (1);
vv.push_back (vp);
}
// dive down into the hierarchy ..
for (std::vector<db::Box>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
if (! v->empty ()) {
bool anything = false;
db::cell_index_type last_ci = std::numeric_limits<db::cell_index_type>::max ();
db::Cell::touching_iterator inst = cell.begin_touching (*v);
while (! inst.at_end ()) {
const db::CellInstArray &cell_inst = inst->cell_inst ();
db::properties_id_type cell_inst_prop = inst->prop_id ();
db::cell_index_type new_ci = cell_inst.object ().cell_index ();
db::Box new_cell_box = mp_layout->cell (new_ci).bbox ();
if (last_ci != new_ci) {
// Hint: don't use any_cell_box on partially visible cells because that will degrade performance
if (new_cell_box.inside (*v)) {
last_ci = new_ci;
anything = any_cell_box (new_ci, m_to_level - (level + 1));
} else {
anything = true;
}
}
if (anything) {
db::Vector a, b;
unsigned long amax, bmax;
bool simplify = false;
if (cell_inst.is_regular_array (a, b, amax, bmax)) {
db::DBox inst_box;
if (cell_inst.is_complex ()) {
inst_box = trans * (cell_inst.complex_trans () * new_cell_box);
} else {
inst_box = trans * new_cell_box;
}
if (((a.x () == 0 && b.y () == 0) || (a.y () == 0 && b.x () == 0)) &&
inst_box.width () < 1.5 && inst_box.height () < 1.5 &&
(amax <= 1 || trans.ctrans (a.length ()) < 1.5) &&
(bmax <= 1 || trans.ctrans (b.length ()) < 1.5)) {
simplify = true;
}
}
if (! simplify) {
// The array (or single instance) must be iterated instance
// by instance
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) {
test_snapshot (0);
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = safe_transformed_box (*v, t.inverted ());
draw_box_properties (drawing_context, new_ci, trans * t, new_vp, level + 1, cell_inst_prop);
}
}
}
++inst;
}
}
}
}
}
}
/**
* @brief A helper function to determine if there are any area-type shapes on the cell below to a certain hierarchy level
*
* @return The number of empty hierarchy levels below the cell (0: there are area-type shapes in this cell)
*/
bool
RedrawThreadWorker::any_shapes (db::cell_index_type cell_index, unsigned int levels)
{
// if the cell is "hidden", it does not need to be drawn
if (int (m_hidden_cells.size ()) > m_cv_index) {
if (m_hidden_cells [m_cv_index].find (cell_index) != m_hidden_cells [m_cv_index].end ()) {
return false;
}
}
// the cache contains all cells that are visited already
RedrawThreadWorker::micro_instance_cache_t::const_iterator c = m_mi_cache.find (std::make_pair (cell_index, levels));
if (c == m_mi_cache.end ()) {
int ret = false;
const db::Cell &cell = mp_layout->cell (cell_index);
if (! cell.shapes (m_layer).begin (db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Boxes, mp_prop_sel, m_inv_prop_sel).at_end ()) {
ret = true;
} else if (levels > 1) {
for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); !cc.at_end () && !ret; ++cc) {
ret = any_shapes (*cc, levels - 1);
}
}
c = m_mi_cache.insert (std::make_pair (std::make_pair (cell_index, levels), ret)).first;
}
return c->second;
}
/**
* @brief A helper function to determine if there are any area-type shapes on the cell below to a certain hierarchy level
*
* @return The number of empty hierarchy levels below the cell (0: there are area-type shapes in this cell)
*/
bool
RedrawThreadWorker::any_cell_box (db::cell_index_type cell_index, unsigned int levels)
{
// if the cell is "hidden", the box must be drawn
if (int (m_hidden_cells.size ()) > m_cv_index) {
if (m_hidden_cells [m_cv_index].find (cell_index) != m_hidden_cells [m_cv_index].end ()) {
return true;
}
}
// the cache contains all cells that are visited already
RedrawThreadWorker::micro_instance_cache_t::const_iterator c = m_mi_cell_box_cache.find (std::make_pair (cell_index, levels));
if (c == m_mi_cell_box_cache.end ()) {
int ret = false;
if (levels > 1) {
const db::Cell &cell = mp_layout->cell (cell_index);
for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); !cc.at_end () && !ret; ++cc) {
ret = any_cell_box (*cc, levels - 1);
}
} else {
ret = true;
}
c = m_mi_cell_box_cache.insert (std::make_pair (std::make_pair (cell_index, levels), ret)).first;
}
return c->second;
}
/**
* @brief A helper function to determine if there are any text shapes on the cell below to a certain hierarchy level
*
* @return The number of empty hierarchy levels below the cell (0: there are texts in this cell)
*/
bool
RedrawThreadWorker::any_text_shapes (db::cell_index_type cell_index, unsigned int levels)
{
// if the cell is "hidden", it does not need to be drawn
if (int (m_hidden_cells.size ()) > m_cv_index) {
if (m_hidden_cells [m_cv_index].find (cell_index) != m_hidden_cells [m_cv_index].end ()) {
return false;
}
}
// the cache contains all cells that are visited already
RedrawThreadWorker::micro_instance_cache_t::const_iterator c = m_mi_text_cache.find (std::make_pair (cell_index, levels));
if (c == m_mi_text_cache.end ()) {
bool ret = false;
const db::Cell &cell = mp_layout->cell (cell_index);
if (! cell.shapes (m_layer).begin (db::ShapeIterator::Texts, mp_prop_sel, m_inv_prop_sel).at_end () ||
(m_show_properties && ! cell.shapes (m_layer).begin (db::ShapeIterator::AllWithProperties, mp_prop_sel, m_inv_prop_sel).at_end ())) {
ret = true;
} else if (levels > 1) {
for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); !cc.at_end () && !ret; ++cc) {
ret = any_text_shapes (*cc, levels - 1);
}
}
c = m_mi_text_cache.insert (std::make_pair (std::make_pair (cell_index, levels), ret)).first;
}
return c->second;
}
static bool
has_zero_bit (const lay::Bitmap *bitmap, unsigned int ixmin, unsigned int iymin, unsigned int ixmax, unsigned int iymax)
{
uint32_t imin = ixmin / 32;
uint32_t imax = ixmax / 32;
if (imin == imax) {
uint32_t m = ((unsigned int) 0xffffffff << (ixmin % 32)) & ((unsigned int) 0xffffffff >> (31 - (ixmax % 32)));
for (unsigned int y = iymin; y <= iymax; ++y) {
if (bitmap->is_scanline_empty (y)) {
return true;
}
if ((bitmap->scanline (y) [imin] & m) != m) {
return true;
}
}
} else {
uint32_t m1 = ((unsigned int) 0xffffffff << (ixmin % 32));
uint32_t m2 = ((unsigned int) 0xffffffff >> (31 - (ixmax % 32)));
for (unsigned int y = iymin; y <= iymax; ++y) {
if (bitmap->is_scanline_empty (y)) {
return true;
}
if ((bitmap->scanline (y) [imin] & m1) != m1) {
return true;
}
for (unsigned int i = imin + 1; i < imax; ++i) {
if (bitmap->scanline (y) [i] != 0xffffffff) {
return true;
}
}
if ((bitmap->scanline (y) [imax] & m2) != m2) {
return true;
}
}
}
return false;
}
static bool
skip_quad (const db::Box &qb, const lay::Bitmap *vertex_bitmap, const db::CplxTrans &trans)
{
double threshold = 32 / trans.mag (); // don't check cells below 32x32 pixels
if (qb.empty () || qb.width () > threshold || qb.height () > threshold || !vertex_bitmap) {
return false;
}
db::DBox qb_trans = (trans * qb) & db::DBox (0, 0, vertex_bitmap->width () - 1.0 - 1e-6, vertex_bitmap->height () - 1.0 - 1e-6);
if (qb_trans.empty ()) {
return true;
}
int ixmin = (unsigned int)(qb_trans.left () + 0.5);
int ixmax = (unsigned int)(qb_trans.right () + 0.5);
int iymin = (unsigned int)(qb_trans.bottom () + 0.5);
int iymax = (unsigned int)(qb_trans.top () + 0.5);
if (! has_zero_bit (vertex_bitmap, ixmin, iymin, ixmax, iymax)) {
return true; // skip
} else {
return false;
}
}
inline void
copy_bitmap (const lay::Bitmap *from, lay::Bitmap *to, int dx, int dy)
{
if (to) {
to->merge (from, dx, dy);
}
}
std::vector<db::Box>
RedrawThreadWorker::search_regions (const db::Box &cell_bbox, const db::Box &vp, int level)
{
std::vector<db::Box> vv;
// create a set of boxes to look into
db::Coord aw = db::coord_traits<db::Coord>::rounded (m_abstract_mode_width / mp_layout->dbu ());
if (vp == db::Box::world ()) {
vv.push_back (vp);
} else if (level == 1 && m_abstract_mode_width > 0 && cell_bbox.width () > db::Box::distance_type (aw * 2) && cell_bbox.height () > db::Box::distance_type (aw * 2)) {
vv.push_back (vp & db::Box (cell_bbox.left (), cell_bbox.bottom (), cell_bbox.left () + aw, cell_bbox.top ()));
vv.push_back (vp & db::Box (cell_bbox.right () - aw, cell_bbox.bottom (), cell_bbox.right (), cell_bbox.top ()));
vv.push_back (vp & db::Box (cell_bbox.left () + aw, cell_bbox.bottom (), cell_bbox.right () - aw, cell_bbox.bottom () + aw));
vv.push_back (vp & db::Box (cell_bbox.left () + aw, cell_bbox.top () - aw, cell_bbox.right () - aw, cell_bbox.top ()));
} else {
vv.push_back (vp);
}
return vv;
}
void
RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vp, int level)
{
if (! m_text_visible) {
return;
}
unsigned int plane_group = 2;
if (drawing_context) {
plane_group = 0;
} else if (m_child_context_enabled && level > 0) {
plane_group = 1;
}
lay::CanvasPlane *fill = 0, *frame = 0, *text = 0, *vertex = 0;
fill = m_planes[0 + plane_group * (planes_per_layer / 3)];
frame = m_planes[1 + plane_group * (planes_per_layer / 3)];
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
// do not draw, if there is nothing to draw
if (mp_layout->cells () <= ci || vp.empty () || mp_layout->cell (ci).bbox (m_layer).empty ()) {
return;
}
if (cell_var_cached (ci, trans)) {
return;
}
std::unique_ptr<lay::Bitmap> opt_bitmap;
lay::Bitmap *vertex_bitmap = dynamic_cast<lay::Bitmap *> (vertex);
if (m_text_lazy_rendering && vertex_bitmap) {
opt_bitmap.reset (new lay::Bitmap (vertex_bitmap->width (), vertex_bitmap->height (), vertex_bitmap->resolution ()));
}
for (std::vector<db::Box>::const_iterator b = vp.begin (); b != vp.end (); ++b) {
draw_text_layer (drawing_context, ci, trans, *b, level, fill, frame, vertex, text, opt_bitmap.get ());
}
}
void
RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, CanvasPlane *fill, CanvasPlane *frame, CanvasPlane *vertex, CanvasPlane *text, lay::Bitmap *opt_bitmap)
{
test_snapshot (0);
const db::Cell &cell = mp_layout->cell (ci);
lay::Renderer &r = *mp_renderer;
// For small bboxes, the cell outline can be reduced ..
db::Box bbox = cell.bbox (m_layer);
if (m_drop_small_cells && drop_cell (cell, trans)) {
// small cell dropped
} else if (! bbox.empty ()) {
bool hidden = (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ());
bool need_to_dive = (level + 1 < m_to_level) && ! hidden;
db::Box cell_bbox = cell.bbox ();
// draw this level
if (level >= m_from_level && level < m_to_level && ! hidden) {
db::DBox dbbox_tot = trans * cell_bbox;
if (m_text_lazy_rendering && ((dbbox_tot.width () < 2.5 && dbbox_tot.height () < 1.5) ||
(dbbox_tot.width () < 1.5 && dbbox_tot.height () < 2.5))) {
bool anything = true;
if (level == 0 && cell_bbox.inside (vp)) {
// Hint: on levels below 0 we enter this procedure only if there is a text
// Hint: don't use any_text_shapes on partially visible cells because that will degrade performance
anything = any_text_shapes (ci, m_to_level - level);
}
// paint the simplified box
if (anything) {
r.draw (trans * bbox, 0, frame, vertex, 0);
if (opt_bitmap) {
r.draw (trans * bbox, 0, 0, opt_bitmap, 0);
}
}
// do not dive further into hierarchy
need_to_dive = false;
} else {
bool text_simplified = m_text_lazy_rendering && (dbbox_tot.width () <= 8.0 || dbbox_tot.height () <= 8.0);
const db::Shapes &shapes = cell.shapes (m_layer);
// In lazy text rendering mode, all texts are only rendered if the cell is "reasonable large".
// Empirically, a reasonable minimum dimension of 8x8 pixels was determined.
// otherwise just a few texts are rendered.
// this is the number of texts to draw in lazy text rendering mode
size_t ntexts = 2;
if (! text_simplified) {
ntexts = std::numeric_limits <size_t>::max ();
}
// create a set of boxes to look into
std::vector<db::Box> vv = search_regions (cell_bbox, vp, level);
// iterate over the shapes
for (std::vector<db::Box>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
if (! v->empty ()) {
db::ShapeIterator shape = shapes.begin_touching (*v, db::ShapeIterator::Texts, mp_prop_sel, m_inv_prop_sel);
while (! shape.at_end () && ntexts > 0) {
test_snapshot (0);
r.draw (*shape, trans, fill, frame, vertex, text);
if (opt_bitmap) {
r.draw (*shape, trans, 0, 0, opt_bitmap, 0);
}
++shape;
--ntexts;
}
if (ntexts == 0) {
break;
}
shape = shapes.begin_touching (*v, db::ShapeIterator::AllWithProperties, mp_prop_sel, m_inv_prop_sel);
while (! shape.at_end () && ntexts > 0) {
test_snapshot (0);
r.draw_propstring (*shape, &mp_layout->properties_repository (), text, trans);
++shape;
--ntexts;
}
if (ntexts == 0) {
break;
}
}
}
}
}
// dive down into the hierarchy ..
if (need_to_dive) {
// create a set of boxes to look into
std::vector<db::Box> vv = search_regions (cell_bbox, vp, level);
// dive down into the hierarchy ..
for (std::vector<db::Box>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
if (! v->empty ()) {
size_t current_quad_id = 0;
bool anything = false;
db::cell_index_type last_ci = std::numeric_limits<db::cell_index_type>::max ();
db::Cell::touching_iterator inst = cell.begin_touching (*v);
while (! inst.at_end ()) {
// skip this quad if we have drawn something here already
size_t qid = inst.quad_id ();
bool skip = false;
if (m_text_lazy_rendering && qid != current_quad_id) {
current_quad_id = qid;
skip = opt_bitmap && skip_quad (inst.quad_box () & bbox, opt_bitmap, trans);
}
if (skip) {
// move on to the next quad
inst.skip_quad ();
} else {
const db::CellInstArray &cell_inst = inst->cell_inst ();
++inst;
db::cell_index_type new_ci = cell_inst.object ().cell_index ();
bool hidden = (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ());
db::Box cell_box = mp_layout->cell (new_ci).bbox (m_layer);
if (! cell_box.empty () && ! hidden) {
db::Vector a, b;
unsigned long amax = 0, bmax = 0;
bool simplify = false;
if (new_ci != last_ci) {
// Hint: don't use any_text_shapes on partially visible cells because that will degrade performance
if (cell_box.inside (vp)) {
last_ci = new_ci;
anything = any_text_shapes (new_ci, m_to_level - (level + 1));
} else {
anything = true;
}
}
if (anything && m_text_lazy_rendering && cell_inst.is_regular_array (a, b, amax, bmax)) {
db::DBox inst_box;
if (cell_inst.is_complex ()) {
inst_box = trans * (cell_inst.complex_trans () * cell_box);
} else {
inst_box = trans * cell_box;
}
if (((a.x () == 0 && b.y () == 0) || (a.y () == 0 && b.x () == 0)) &&
inst_box.width () < 1.5 && inst_box.height () < 1.5 &&
(amax <= 1 || trans.ctrans (a.length ()) < 1.5) &&
(bmax <= 1 || trans.ctrans (b.length ()) < 1.5)) {
simplify = true;
}
}
db::box_convert <db::CellInst> bc (*mp_layout, m_layer);
if (simplify) {
// The array can be simplified ..
db::Box bbox = cell_inst.bbox (bc);
if (vertex) {
r.draw (bbox, trans, vertex, vertex, 0, 0);
}
} else if (anything) {
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) {
if (! m_draw_array_border_instances ||
p.index_a () <= 0 || (unsigned long)p.index_a () == amax - 1 || p.index_b () <= 0 || (unsigned long)p.index_b () == bmax - 1) {
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = safe_transformed_box (*v, t.inverted ());
draw_text_layer (drawing_context, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text, opt_bitmap);
}
}
}
}
}
}
}
}
}
}
}
template <class Array>
bool draw_array_simplified (lay::Renderer *r, const db::Shape &array_shape, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, const db::CplxTrans &trans)
{
typename Array::tag tag;
const Array *array = array_shape.basic_ptr (tag);
db::Vector a, b;
unsigned long na = 0, nb = 0;
bool is_regular = array->is_regular_array (a, b, na, nb);
size_t n = array->size ();
if (n >= 2) {
db::box_convert <typename Array::object_type> bc;
db::DBox shape_box_trans = trans * bc (array->object ());
if (shape_box_trans.width () < 1.5 && shape_box_trans.height () < 1.5) {
if (is_regular &&
((a.x () == 0 && b.y () == 0) || (a.y () == 0 && b.x () == 0)) &&
(na <= 1 || trans.ctrans (a.length ()) < 1.5) &&
(nb <= 1 || trans.ctrans (b.length ()) < 1.5)) {
db::Box array_box = array_shape.bbox ();
r->draw (array_box, trans, frame, frame, 0, 0);
r->draw (array_box, trans, vertex, vertex, 0, 0);
return true;
} else if (is_regular &&
(a.x () == 0 || a.y () == 0) &&
na > 1 && trans.ctrans (a.length ()) < 1.5) {
Array a1 (array->object (), array->front (), a, db::Vector (0, 0), na, 1);
db::Box abox = a1.bbox (bc);
for (unsigned long i = 0; i < nb; ++i) {
r->draw (abox, trans, frame, frame, 0, 0);
r->draw (abox, trans, vertex, vertex, 0, 0);
abox.move (b);
}
return true;
} else if (is_regular &&
(b.x () == 0 || b.y () == 0) &&
nb > 1 && trans.ctrans (b.length ()) < 1.5) {
Array a1 (array->object (), array->front (), db::Vector (0, 0), b, 1, nb);
db::Box abox = a1.bbox (bc);
for (unsigned long i = 0; i < na; ++i) {
r->draw (abox, trans, frame, frame, 0, 0);
r->draw (abox, trans, vertex, vertex, 0, 0);
abox.move (a);
}
return true;
} else {
db::DBox array_box_trans = trans * array_shape.bbox ();
if ((array_box_trans.height () < 1.5 && array_box_trans.width () < 3.5) ||
(array_box_trans.height () < 3.5 && array_box_trans.width () < 1.5)) {
r->draw (array_box_trans, frame, frame, 0, 0);
r->draw (array_box_trans, vertex, vertex, 0, 0);
return true;
}
}
}
}
return false;
}
void
RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vv, int level,
lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot)
{
const db::Cell &cell = mp_layout->cell (ci);
lay::Renderer &r = *mp_renderer;
const db::Box &bbox = cell.bbox (m_layer);
const lay::Bitmap *vertex_bitmap = dynamic_cast<const lay::Bitmap *> (vertex);
// draw this level
if (level >= from_level && level < to_level) {
// draw the shapes or insert into the cell cache.
for (std::vector<db::Box>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
if (v->empty ()) {
continue;
}
const db::Shapes &shapes = cell.shapes (m_layer);
db::Shape last_array;
size_t current_quad_id = 0;
size_t current_array_quad_id = 0;
db::ShapeIterator shape (shapes.begin_touching (*v, db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths, mp_prop_sel, m_inv_prop_sel));
while (! shape.at_end ()) {
test_snapshot (update_snapshot);
// skip this quad if we have drawn something here already
size_t qid = shape.quad_id ();
bool skip = false;
if (vertex_bitmap && qid != current_quad_id) {
current_quad_id = qid;
skip = skip_quad (shape.quad_box () & bbox, vertex_bitmap, trans);
}
if (skip) {
shape.skip_quad ();
continue;
}
if (shape.in_array ()) {
if (last_array != shape.array ()) {
last_array = shape.array ();
current_array_quad_id = 0;
bool simplified = false;
if (last_array.type () == db::Shape::PolygonPtrArray) {
simplified = draw_array_simplified<db::Shape::polygon_ptr_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
} else if (last_array.type () == db::Shape::SimplePolygonPtrArray) {
simplified = draw_array_simplified<db::Shape::simple_polygon_ptr_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
} else if (last_array.type () == db::Shape::PathPtrArray) {
simplified = draw_array_simplified<db::Shape::path_ptr_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
} else if (last_array.type () == db::Shape::BoxArray) {
simplified = draw_array_simplified<db::Shape::box_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
} else if (last_array.type () == db::Shape::ShortBoxArray) {
simplified = draw_array_simplified<db::Shape::short_box_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
}
if (simplified) {
shape.finish_array ();
// continue with the next shape, array or quad
continue;
}
}
} else {
current_array_quad_id = 0;
}
// try whether the array quad can be simplified
size_t aqid = shape.array_quad_id ();
if (aqid != 0 && aqid != current_array_quad_id) {
current_array_quad_id = aqid;
db::DBox qbbox = trans * shape.array_quad_box ();
if (qbbox.width () < 1.5 && qbbox.height () < 1.5) {
// draw a single box instead of the quad
mp_renderer->draw (qbbox, fill, frame, vertex, text);
shape.skip_array_quad ();
// continue with the next shape, array or quad
continue;
}
}
mp_renderer->draw (*shape, trans, fill, frame, vertex, text);
++shape;
}
}
}
// dive down into the hierarchy ..
if (level + 1 < to_level) {
db::box_convert <db::CellInst> bc (*mp_layout, m_layer);
// dive down into the hierarchy ..
for (std::vector<db::Box>::const_iterator v = vv.begin (); v != vv.end (); ++v) {
if (v->empty ()) {
continue;
}
size_t current_quad_id = 0;
db::cell_index_type last_ci = std::numeric_limits<db::cell_index_type>::max ();
bool anything = false;
db::Cell::touching_iterator inst = cell.begin_touching (*v);
while (! inst.at_end ()) {
test_snapshot (update_snapshot);
// skip this quad if we have drawn something here already
size_t qid = inst.quad_id ();
bool skip = false;
if (qid != current_quad_id) {
current_quad_id = qid;
skip = skip_quad (inst.quad_box () & bbox, vertex_bitmap, trans);
}
if (skip) {
// move on to the next quad
inst.skip_quad ();
} else {
const db::CellInstArray &cell_inst = inst->cell_inst ();
++inst;
db::cell_index_type new_ci = cell_inst.object ().cell_index ();
bool hidden = (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ());
db::Box new_cell_box = mp_layout->cell (new_ci).bbox (m_layer);
if (! new_cell_box.empty () && ! hidden) {
db::Vector a, b;
unsigned long amax = 0, bmax = 0;
bool simplify = false;
if (last_ci != new_ci) {
// Hint: don't use any_text_shapes on partially visible cells because that will degrade performance
if (new_cell_box.inside (*v)) {
last_ci = new_ci;
anything = any_shapes (new_ci, to_level - (level + 1));
} else {
anything = true;
}
}
if (anything && cell_inst.is_regular_array (a, b, amax, bmax)) {
db::DBox inst_box;
if (cell_inst.is_complex ()) {
inst_box = trans * (cell_inst.complex_trans () * new_cell_box);
} else {
inst_box = trans * new_cell_box;
}
if (((a.x () == 0 && b.y () == 0) || (a.y () == 0 && b.x () == 0)) &&
inst_box.width () < 1.5 && inst_box.height () < 1.5 &&
(amax <= 1 || trans.ctrans (a.length ()) < 1.5) &&
(bmax <= 1 || trans.ctrans (b.length ()) < 1.5)) {
simplify = true;
}
}
if (simplify) {
// The array can be simplified ..
db::Box bbox = cell_inst.bbox (bc);
if (frame) {
r.draw (bbox, trans, frame, frame, 0, 0);
}
if (vertex) {
r.draw (bbox, trans, vertex, vertex, 0, 0);
}
} else if (anything) {
size_t qid = 0;
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) {
if (! m_draw_array_border_instances ||
p.index_a () <= 0 || (unsigned long)p.index_a () == amax - 1 || p.index_b () <= 0 || (unsigned long)p.index_b () == bmax - 1) {
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = safe_transformed_box (*v, t.inverted ());
draw_layer (from_level, to_level, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text, update_snapshot);
if (p.quad_id () > 0 && p.quad_id () != qid) {
qid = p.quad_id ();
// if the quad is very small we don't gain anything from looking further into the quad - skip this one
db::DBox qb = trans * cell_inst.quad_box (p, bc);
if (qb.width () < 1.0 && qb.height () < 1.0) {
p.skip_quad ();
continue;
}
}
}
++p;
}
}
}
}
}
}
}
}
class UpdateSnapshotWithCache
: public UpdateSnapshotCallback
{
public:
UpdateSnapshotWithCache (const UpdateSnapshotCallback *parent, const db::CplxTrans *trans, const CellCacheInfo *info, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text)
: mp_parent (parent), mp_trans (trans), mp_info (info), mp_fill (fill), mp_frame (frame), mp_vertex (vertex), mp_text (text)
{
// .. nothing yet ..
}
void trigger () const
{
if (mp_parent) {
mp_parent->trigger ();
}
db::Point t = db::Point (mp_info->offset + mp_trans->disp ());
copy_bitmap(mp_info->fill, dynamic_cast<lay::Bitmap *> (mp_fill), t.x (), t.y ());
copy_bitmap(mp_info->frame, dynamic_cast<lay::Bitmap *> (mp_frame), t.x (), t.y ());
copy_bitmap(mp_info->vertex, dynamic_cast<lay::Bitmap *> (mp_vertex), t.x (), t.y ());
copy_bitmap(mp_info->text, dynamic_cast<lay::Bitmap *> (mp_text), t.x (), t.y ());
}
private:
const UpdateSnapshotCallback *mp_parent;
const db::CplxTrans *mp_trans;
const CellCacheInfo *mp_info;
lay::CanvasPlane *mp_fill;
lay::CanvasPlane *mp_frame;
lay::CanvasPlane *mp_vertex;
lay::CanvasPlane *mp_text;
};
void
RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &vp, int level,
lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot)
{
// do not draw, if there is nothing to draw
if (mp_layout->cells () <= ci || vp.empty ()) {
return;
}
if (cell_var_cached (ci, trans)) {
return;
}
for (std::vector<db::Box>::const_iterator b = vp.begin (); b != vp.end (); ++b) {
draw_layer (from_level, to_level, ci, trans, *b, level, fill, frame, vertex, text, update_snapshot);
}
}
void
RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level,
lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot)
{
test_snapshot (update_snapshot);
const db::Cell &cell = mp_layout->cell (ci);
db::Box bbox = cell.bbox (m_layer);
db::Box cell_bbox = cell.bbox ();
// Nothing to draw
if (bbox.empty ()) {
return;
}
// For small bboxes, the cell outline can be reduced ..
if (m_drop_small_cells && drop_cell (cell, trans)) {
return;
}
// Don't draw hidden cells
bool hidden = (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ());
if (hidden) {
return;
}
// draw this level
if (level >= from_level && level < to_level) {
// optimize very small cells
db::DBox dbbox = trans * bbox;
if ((dbbox.width () < 2.5 && dbbox.height () < 1.5) ||
(dbbox.width () < 1.5 && dbbox.height () < 2.5)) {
if (bbox.overlaps (vp)) {
bool anything = true;
if (level == 0) {
// Hint: on levels below 0 we know that there is anything. Otherwise we would not enter this procedure
// Hint: don't use any_text_shapes on partially visible cells because that will degrade performance
anything = any_shapes (ci, m_to_level - level);
}
if (anything) {
// any shapes here: paint bbox for simplification
mp_renderer->draw (dbbox, 0, frame, vertex, 0);
}
}
} else {
// create a set of boxes to look into
std::vector<db::Box> vv = search_regions (cell_bbox, vp, level);
// use the presence of a lay::Bitmap for the drawing plane as an indicator that we can cache the
// drawings
bool can_cache = (m_bitmap_caching && dynamic_cast<lay::Bitmap *> (fill) != 0);
// don't cache if the cell is not fully inside the search region
if (vv.size () > 1 || ! cell_bbox.inside (vv.front ())) {
can_cache = false;
}
// only cache if we have more than one instance at all
if (can_cache && level > 0) {
db::Cell::parent_inst_iterator p = cell.begin_parent_insts ();
size_t n;
for (n = 0; !p.at_end () && n < 2; ++n)
;
if (n <= 1) {
can_cache = false;
}
}
if (can_cache) {
db::CplxTrans trans_wo_disp = trans;
trans_wo_disp.disp (db::DVector ());
// if we have the cell cached, use the cached bitmap
CellCacheKey key (to_level - level, ci, trans_wo_disp);
cell_cache_t::iterator cached_cell = m_cell_cache.find (key);
if (cached_cell == m_cell_cache.end ()) {
// put the cell into the cache
cached_cell = m_cell_cache.insert (std::make_pair (key, CellCacheInfo ())).first;
db::DBox cell_box_trans = trans_wo_disp * cell_bbox;
// Hint: this rounding scheme guarantees a integer-pixel shift vector at least for the first instance
db::DPoint d = cell_box_trans.lower_left () + trans.disp ();
d = db::DPoint (floor (d.x ()), floor (d.y ()));
cached_cell->second.offset = d - trans.disp ();
db::CplxTrans drawing_trans = trans_wo_disp;
drawing_trans.disp (db::DPoint () - cached_cell->second.offset);
int width = int (cell_box_trans.width () + 3); // +3 = one pixel for a one-pixel frame at both sides and one for safety
int height = int (cell_box_trans.height () + 3);
cached_cell->second.fill = new lay::Bitmap (width, height, 1.0);
cached_cell->second.frame = new lay::Bitmap (width, height, 1.0);
cached_cell->second.vertex = new lay::Bitmap (width, height, 1.0);
cached_cell->second.text = new lay::Bitmap (width, height, 1.0);
// this object is responsible for doing updates when a snapshot is taken
UpdateSnapshotWithCache update_cached_snapshot (update_snapshot, &trans, &cached_cell->second, fill, frame, vertex, text);
draw_layer_wo_cache (from_level, to_level, ci, drawing_trans, vv, level, cached_cell->second.fill, cached_cell->second.frame, cached_cell->second.vertex, cached_cell->second.text, &update_cached_snapshot);
}
cached_cell->second.hits++;
db::Point t = db::Point (cached_cell->second.offset + trans.disp ());
copy_bitmap(cached_cell->second.fill, dynamic_cast<lay::Bitmap *> (fill), t.x (), t.y ());
copy_bitmap(cached_cell->second.frame, dynamic_cast<lay::Bitmap *> (frame), t.x (), t.y ());
copy_bitmap(cached_cell->second.vertex, dynamic_cast<lay::Bitmap *> (vertex), t.x (), t.y ());
copy_bitmap(cached_cell->second.text, dynamic_cast<lay::Bitmap *> (text), t.x (), t.y ());
} else {
draw_layer_wo_cache (from_level, to_level, ci, trans, vv, level, fill, frame, vertex, text, update_snapshot);
}
}
} else {
// draw stuff below (not on this level)
std::vector<db::Box> vv;
vv.push_back (vp);
draw_layer_wo_cache (from_level, to_level, ci, trans, vv, level, fill, frame, vertex, text, update_snapshot);
}
}
void
RedrawThreadWorker::draw_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector<db::Box> &redraw_regions, int level)
{
if (drawing_context) {
if (m_to_level > m_from_level) {
lay::CanvasPlane *fill = 0, *frame = 0, *text = 0, *vertex = 0;
int plane_group = 0;
fill = m_planes[0 + plane_group * (planes_per_layer / 3)];
frame = m_planes[1 + plane_group * (planes_per_layer / 3)];
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
draw_layer (m_from_level, m_to_level, ci, trans, redraw_regions, level, fill, frame, vertex, text, 0);
}
} else if (! m_child_context_enabled) {
if (m_to_level > m_from_level) {
lay::CanvasPlane *fill = 0, *frame = 0, *text = 0, *vertex = 0;
int plane_group = 2;
fill = m_planes[0 + plane_group * (planes_per_layer / 3)];
frame = m_planes[1 + plane_group * (planes_per_layer / 3)];
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
draw_layer (m_from_level, m_to_level, ci, trans, redraw_regions, level, fill, frame, vertex, text, 0);
}
} else {
if (1 > m_from_level) {
lay::CanvasPlane *fill = 0, *frame = 0, *text = 0, *vertex = 0;
int plane_group = 2;
fill = m_planes[0 + plane_group * (planes_per_layer / 3)];
frame = m_planes[1 + plane_group * (planes_per_layer / 3)];
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
draw_layer (m_from_level, 1, ci, trans, redraw_regions, level, fill, frame, vertex, text, 0);
}
if (m_to_level > 1) {
lay::CanvasPlane *fill = 0, *frame = 0, *text = 0, *vertex = 0;
int plane_group = 1;
fill = m_planes[0 + plane_group * (planes_per_layer / 3)];
frame = m_planes[1 + plane_group * (planes_per_layer / 3)];
text = m_planes[2 + plane_group * (planes_per_layer / 3)];
vertex = m_planes[3 + plane_group * (planes_per_layer / 3)];
draw_layer (1, m_to_level, ci, trans, redraw_regions, level, fill, frame, vertex, text, 0);
}
}
}
bool
RedrawThreadWorker::drop_cell (const db::Cell &cell, const db::CplxTrans &trans)
{
db::DBox bbox = trans * cell.bbox ();
double value = 0;
if (m_drop_small_cells_cond == lay::LayoutView::DSC_Min) {
value = std::min (bbox.width (), bbox.height ());
} else if (m_drop_small_cells_cond == lay::LayoutView::DSC_Max) {
value = std::max (bbox.width (), bbox.height ());
} else {
value = bbox.width () + bbox.height ();
}
return (value < double (m_drop_small_cells_value));
}
bool
RedrawThreadWorker::cell_var_cached (db::cell_index_type ci, const db::CplxTrans &trans)
{
if (mp_cell_var_cache) {
// Use the native transformation (just including cell instantiation components) to enable
// fuzzy comparison of floating-point coordinates: this requires a well-defined unit system to
// allow the definition of an uncertainty value.
db::CplxTrans db_trans ((m_vp_trans * mp_layout->dbu ()).inverted () * db::DCplxTrans (trans));
if (mp_cell_var_cache->find (std::make_pair (db_trans, ci)) != mp_cell_var_cache->end ()) {
++m_cache_hits;
return true;
}
++m_cache_misses;
mp_cell_var_cache->insert (std::make_pair (db_trans, ci));
}
return false;
}
void
RedrawThreadWorker::iterate_variants (const std::vector <db::Box> &redraw_regions, db::cell_index_type ci, db::CplxTrans trans, void (RedrawThreadWorker::*what) (bool, db::cell_index_type, const db::CplxTrans &, const std::vector<db::Box> &, int))
{
// save current state
int from_level = m_from_level;
int to_level = m_to_level;
// if a context path is given, we adjust the levels such that the target (not the context
// cell) is drawn and the context cell is visible through addressing negative levels. The
// iterate_variants_rec methods takes care of converting the negative hierarchy levels into
// traversals along the context path bottom up.
size_t ctx_path_length = m_cellviews [m_cv_index].specific_path ().size ();
if (ctx_path_length > 0) {
m_from_level -= int (ctx_path_length);
m_to_level -= int (ctx_path_length);
trans = trans * m_cellviews [m_cv_index].context_trans ();
}
if (m_from_level_default < 0 || ctx_path_length > 0) {
// if we start from above the hierarchy, we need to establish a
// cell variant cache to at least avoid painting the current cell
// multiple times.
std::set <std::pair <db::CplxTrans, db::cell_index_type>, lay::CellVariantCacheCompare> cell_var_cache;
mp_cell_var_cache = &cell_var_cache;
// Use the cell variant cache to exclude the basic instance from the
// drawing in the first pass.
cell_var_cache.insert (std::make_pair (db::CplxTrans (m_cellviews [m_cv_index].context_trans ()), ci));
m_cache_hits = m_cache_misses = 0;
// draw the context for the current instance
iterate_variants_rec (redraw_regions, ci, trans, 0, what, true);
cell_var_cache.clear ();
// draw the current instance without context (using a minimum of from_level=0 for this)
mp_cell_var_cache = 0;
int fl = m_from_level;
if (m_from_level < 0) {
m_from_level = 0;
}
iterate_variants_rec (redraw_regions, ci, trans, 0, what, false);
m_from_level = fl;
if (tl::verbosity () >= 40) {
tl::info << tl::to_string (QObject::tr ("Cell variant cache hits/misses: ")) << m_cache_hits << "/" << m_cache_misses;
}
} else {
mp_cell_var_cache = 0;
iterate_variants_rec (redraw_regions, ci, trans, 0, what, false);
}
// restore state
m_from_level = from_level;
m_to_level = to_level;
}
void
RedrawThreadWorker::iterate_variants_rec (const std::vector <db::Box> &redraw_regions, db::cell_index_type ci, const db::CplxTrans &trans, int level, void (RedrawThreadWorker::*what) (bool, db::cell_index_type, const db::CplxTrans &, const std::vector<db::Box> &, int), bool drawing_context)
{
db::Cell::parent_inst_iterator p = mp_layout->cell (ci).begin_parent_insts ();
int context_path_length = int (m_cellviews [m_cv_index].specific_path ().size ());
if ((drawing_context || level > m_from_level) && level + context_path_length > 0) {
// pull an specific instance from the instance stack and move this one up
const db::InstElement &ie = m_cellviews [m_cv_index].specific_path ().end () [level - 1];
db::cell_index_type new_ci;
if (level + context_path_length > 1) {
new_ci = m_cellviews [m_cv_index].specific_path ().end () [level - 2].inst_ptr.cell_index ();
} else {
new_ci = m_cellviews [m_cv_index].ctx_cell_index ();
}
db::ICplxTrans t (ie.complex_trans ());
iterate_variants_rec (redraw_regions, new_ci, trans * t.inverted (), level - 1, what, drawing_context);
} else if (level > (drawing_context ? (m_from_level_default - context_path_length) : m_from_level) && ! p.at_end ()) {
// one level up ..
while (! p.at_end ()) {
db::Cell::cell_inst_array_type pi = (*p).inst ();
db::cell_index_type new_ci = pi.object ().cell_index ();
for (db::Cell::cell_inst_array_type::iterator pp = pi.begin (); ! pp.at_end (); ++pp) {
db::ICplxTrans t (pi.complex_trans (*pp));
iterate_variants_rec (redraw_regions, new_ci, trans * t, level - 1, what, drawing_context);
}
++p;
}
} else {
std::vector<db::Box> actual_regions;
actual_regions.reserve (redraw_regions.size ());
for (std::vector<db::Box>::const_iterator rr = redraw_regions.begin (); rr != redraw_regions.end (); ++rr) {
db::Coord lim = std::numeric_limits<db::Coord>::max ();
db::DBox world (trans * db::Box (db::Point (-lim, -lim), db::Point (lim, lim)));
db::Box vp = db::Box (trans.inverted () * (world & db::DBox (*rr)));
vp &= mp_layout->cell (ci).bbox (); // this avoids problems when accessing designs through very large viewports
if (! vp.empty ()) {
actual_regions.push_back (vp);
}
}
if (! actual_regions.empty ()) {
(this->*what) (drawing_context, ci, trans, actual_regions, level);
}
}
}
}