/* KLayout Layout Viewer Copyright (C) 2006-2017 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 "layLayerTreeModel.h" #include "layLayoutView.h" #include "layBitmapsToImage.h" #include "dbLayoutUtils.h" #include "tlLog.h" #include "tlTimer.h" #include "tlGlobPattern.h" #include #include #include #include #include namespace lay { // -------------------------------------------------------------------- // EmptyWithinViewCache implementation EmptyWithinViewCache::EmptyWithinViewCache () { // .. nothing yet .. } void EmptyWithinViewCache::clear () { m_cache.clear(); } bool EmptyWithinViewCache::is_empty_within_view (const db::Layout *layout, unsigned int cell_index, const db::Box &box, unsigned int layer) { std::pair c = m_cache.insert (std::make_pair(std::make_pair(std::make_pair(layout, cell_index), box), std::set ())); if (c.second) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (QObject::tr ("Recomputing layers with shapes in view"))); const db::Cell &cell (layout->cell (cell_index)); // determine layers with shapes on the given layout and within the given box std::vector ll; for (db::Layout::layer_iterator l = layout->begin_layers (); l != layout->end_layers (); ++l) { if (cell.bbox ((*l).first).empty ()) { c.first->second.insert ((*l).first); } else if (! cell.shapes ((*l).first).begin_touching (box, db::ShapeIterator::All, 0, false).at_end ()) { ; } else { ll.push_back ((*l).first); } } m_cells_done.resize (layout->cells (), false); determine_empty_layers(layout, cell_index, box, ll); m_cells_done = std::vector (); c.first->second.insert (ll.begin (), ll.end ()); } return c.first->second.find(layer) != c.first->second.end(); } void EmptyWithinViewCache::determine_empty_layers (const db::Layout *layout, unsigned int cell_index, const db::Box &box, std::vector &layers) { if (layers.empty ()) { return; } // Hint: this implementation counts all hierarchy levels - also the ones not shown ... db::box_convert bc (*layout); db::Cell::touching_iterator inst = layout->cell (cell_index).begin_touching (box); while (! inst.at_end () && ! layers.empty ()) { db::cell_index_type ci = inst->cell_index (); if (! m_cells_done [ci]) { const db::Cell &cell (layout->cell (ci)); if (inst->bbox (bc).inside (box)) { // The instance is fully inside the search box: remove the non-empty layers from the // list and mark the cell as "done". std::vector::iterator lw = layers.begin (); for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { if (cell.bbox (*l).empty ()) { *lw++ = *l; } } layers.erase (lw, layers.end ()); m_cells_done [ci] = true; } else { // Remove every layer from the list which has at least one shape on it within the box and // temporarily create a list of layers with only the relevant layers in ll std::vector ll; ll.reserve (layers.size ()); std::vector::iterator lw = layers.begin (); for (std::vector::const_iterator l = layers.begin (); l != layers.end (); ++l) { if (cell.bbox (*l).empty ()) { *lw++ = *l; } else { ll.push_back (*l); } } layers.erase (lw, layers.end ()); if (! ll.empty ()) { db::CellInstArray::iterator inst_array = inst->cell_inst ().begin_touching (box, bc); while (! inst_array.at_end () && ! ll.empty ()) { db::Box new_box = db::Box (inst->complex_trans (*inst_array).inverted () * box); // remove all layers which became populated in that instance std::vector::iterator llw = ll.begin (); for (std::vector::const_iterator l = ll.begin (); l != ll.end (); ++l) { if (cell.shapes (*l).begin_touching (new_box, db::ShapeIterator::All, 0, false).at_end ()) { *llw++ = *l; } } ll.erase (llw, ll.end ()); determine_empty_layers (layout, inst->cell_index (), new_box, ll); ++inst_array; } // Join the lists of remaining layers layers.insert (layers.end (), ll.begin (), ll.end ()); } } } ++inst; } } // -------------------------------------------------------------------- // LayerTreeModel implementation LayerTreeModel::LayerTreeModel (QWidget *parent, lay::LayoutView *view) : QAbstractItemModel (parent), mp_view (view), m_id_start (0), m_id_end (0), m_phase ((unsigned int) -1), m_test_shapes_in_view (false) { // .. nothing yet .. } LayerTreeModel::~LayerTreeModel () { // .. nothing yet .. } void LayerTreeModel::set_phase (unsigned int ph) { m_phase = ph; } void LayerTreeModel::set_font (const QFont &font) { m_font = font; signal_data_changed (); } void LayerTreeModel::set_text_color (QColor color) { m_text_color = color; signal_data_changed (); } void LayerTreeModel::set_background_color (QColor background) { m_background_color = background; signal_data_changed (); } Qt::ItemFlags LayerTreeModel::flags (const QModelIndex &index) const { return QAbstractItemModel::flags (index); } int LayerTreeModel::columnCount (const QModelIndex &) const { return 2; } QVariant LayerTreeModel::headerData (int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const { return QVariant (); } int LayerTreeModel::rowCount (const QModelIndex &parent) const { if (mp_view->layer_model_updated ()) { if (parent.isValid ()) { lay::LayerPropertiesConstIterator iter (iterator (parent)); if (iter.is_null () || iter.at_end ()) { return 0; } else { return iter->end_children () - iter->begin_children (); } } else { int n = int (mp_view->get_properties ().end_const () - mp_view->get_properties ().begin_const ()); return n; } } else { return 0; } } QModelIndex LayerTreeModel::index (int row, int column, const QModelIndex &parent) const { if (row >= 0 && row < rowCount (parent)) { if (parent.isValid ()) { lay::LayerPropertiesConstIterator iter (iterator (parent)); if (iter.is_null () || iter.at_end ()) { return QModelIndex (); } else { iter.down_first_child (); iter.next_sibling (row); return createIndex (row, column, (void *) (iter.uint () + m_id_start)); } } else { lay::LayerPropertiesConstIterator iter (mp_view->begin_layers ()); iter.next_sibling (row); return createIndex (row, column, (void *) (iter.uint () + m_id_start)); } } else { return QModelIndex (); } } void LayerTreeModel::clear_locate () { m_selected_indexes.clear (); m_current_index = m_selected_indexes.begin (); m_selected_ids.clear (); signal_data_changed (); } QModelIndex LayerTreeModel::locate_next () { if (m_current_index == m_selected_indexes.end ()) { return QModelIndex (); } else { ++m_current_index; if (m_current_index == m_selected_indexes.end ()) { m_current_index = m_selected_indexes.begin (); } return *m_current_index; } } QModelIndex LayerTreeModel::locate_prev () { if (m_current_index == m_selected_indexes.end ()) { return QModelIndex (); } else { if (m_current_index == m_selected_indexes.begin ()) { m_current_index = m_selected_indexes.end (); } --m_current_index; return *m_current_index; } } void LayerTreeModel::search_children (const tl::GlobPattern &pattern, const QModelIndex &parent, bool recurse) { int children = rowCount (parent); for (int i = 0; i < children; ++i) { QModelIndex child = index (i, 0, parent); lay::LayerPropertiesConstIterator iter (iterator (child)); if (!iter.is_null () && !iter.at_end () && pattern.match (iter->display_string (mp_view, true /*real*/))) { m_selected_indexes.push_back (child); } if (recurse && iter->has_children ()) { search_children (pattern, child, recurse); } } } QModelIndex LayerTreeModel::locate (const char *name, bool glob_pattern, bool case_sensitive, bool top_only) { m_selected_indexes.clear (); tl::GlobPattern p = tl::GlobPattern (std::string (name)); p.set_case_sensitive (case_sensitive); p.set_exact (!glob_pattern); p.set_header_match (true); search_children (p, QModelIndex (), !top_only); m_selected_ids.clear (); for (std::vector::const_iterator i = m_selected_indexes.begin (); i != m_selected_indexes.end (); ++i) { m_selected_ids.insert (size_t (i->internalPointer ())); } signal_data_changed (); m_current_index = m_selected_indexes.begin (); if (m_current_index == m_selected_indexes.end ()) { return QModelIndex (); } else { return *m_current_index; } } void LayerTreeModel::signal_data_changed () { m_test_shapes_cache.clear (); emit dataChanged (upperLeft (), bottomRight ()); } void LayerTreeModel::signal_begin_layer_changed () { m_id_start = m_id_end; // means: model is invalid m_test_shapes_cache.clear (); emit layoutAboutToBeChanged (); } void LayerTreeModel::signal_layer_changed () { // establish a new range of valid iterator indices m_id_start = m_id_end; // TODO: is there a more efficient way to compute that? size_t max_id = 0; for (lay::LayerPropertiesConstIterator iter = mp_view->get_properties(); ! iter.at_end (); ++iter) { max_id = std::max (iter.uint (), max_id); } m_id_end += max_id + 1; m_test_shapes_cache.clear (); emit layoutChanged (); } QModelIndex LayerTreeModel::upperLeft () const { if (mp_view->layer_model_updated ()) { lay::LayerPropertiesConstIterator iter (mp_view->begin_layers ()); iter.next_sibling (0); return createIndex (0, 0, (void *) (iter.uint () + m_id_start)); } else { return QModelIndex (); } } QModelIndex LayerTreeModel::bottomRight () const { if (mp_view->layer_model_updated ()) { // navigate to the last top-level item lay::LayerPropertiesConstIterator iter (mp_view->begin_layers ()); int n = int (mp_view->get_properties ().end_const () - mp_view->get_properties ().begin_const ()) - 1; iter.next_sibling (n); // navigate to the last child QModelIndex p = createIndex (n, 1, (void *) (iter.uint () + m_id_start)); int nr = 0; while (p.isValid () && (nr = rowCount (p)) > 0) { p = index (nr - 1, 0, p); } return p; } else { return QModelIndex (); } } QModelIndex LayerTreeModel::parent (const QModelIndex &index) const { try { if (mp_view->layer_model_updated ()) { lay::LayerPropertiesConstIterator iter (iterator (index)); if (iter.is_null () || iter.at_end ()) { return QModelIndex (); } else { iter.up (); if (iter.is_null ()) { return QModelIndex (); } else { // It is important that the column index of the parent is 0. // Otherwise the tree view will not behave as expected. return createIndex (iter.child_index (), 0, (void *) (iter.uint () + m_id_start)); } } } else { return QModelIndex (); } } catch (tl::Exception &ex) { // this can happen because some internal indices might not be in place properly - in particular the // element the mouse was over (hover index) tl::warn << ex.msg (); return QModelIndex (); } } /** * @brief A helper function to create an image from a single bitmap */ static void single_bitmap_to_image (const lay::ViewOp &view_op, lay::Bitmap &bitmap, QImage *pimage, const lay::DitherPattern &dither_pattern, const lay::LineStyles &line_styles, unsigned int width, unsigned int height) { std::vector view_ops; view_ops.push_back (view_op); std::vector pbitmaps; pbitmaps.push_back (&bitmap); lay::bitmaps_to_image (view_ops, pbitmaps, dither_pattern, line_styles, pimage, width, height, false, 0); } bool LayerTreeModel::empty_predicate (const QModelIndex &index) const { lay::LayerPropertiesConstIterator iter (iterator (index)); if (iter.is_null () || iter.at_end ()) { return true; } else if (iter->is_cell_box_layer () || iter->is_standard_layer ()) { return iter->bbox ().empty (); } else { // special purpose layers are always visible return false; } } bool LayerTreeModel::empty_within_view_predicate (const QModelIndex &index) const { lay::LayerPropertiesConstIterator iter (iterator (index)); if (iter.is_null () || iter.at_end ()) { return false; } else if (iter->is_standard_layer ()) { int cv_index = iter->cellview_index (); if (! mp_view->cellview (cv_index).is_valid ()) { return true; } const lay::CellView &cv = mp_view->cellview (cv_index); const db::Layout &layout = cv->layout (); int layer_id = iter->layer_index (); if (! layout.is_valid_layer (layer_id)) { return true; } db::cell_index_type ci = cv.cell_index (); const db::Cell &cell = layout.cell (ci); db::ICplxTrans ctx_trans = cv.context_trans (); std::vector trans = iter->trans (); const lay::Viewport &vp = mp_view->viewport (); db::DCplxTrans vp_trans = vp.trans (); int width = vp.width (); int height = vp.height (); for (std::vector::const_iterator t = trans.begin (); t != trans.end (); ++t) { db::CplxTrans ct = vp_trans * *t * db::CplxTrans(layout.dbu ()) * ctx_trans; // the following scheme to compute the region avoids problems with accessing designs through very large viewports: db::Coord lim = std::numeric_limits::max (); db::DBox world (ct * db::Box (db::Point (-lim, -lim), db::Point (lim, lim))); db::Box region = ct.inverted () * (world & db::DBox (db::DPoint (0.0, 0.0), db::DPoint (width, height))); region &= cell.bbox (); if (! m_test_shapes_cache.is_empty_within_view (&layout, ci, region, layer_id)) { return false; } } return true; } else if (iter->is_cell_box_layer ()) { // There is no "within view" method for cell frame layers currently. return iter->bbox ().empty (); } else { // Other special purpose layers are always visible return false; } } QVariant LayerTreeModel::data (const QModelIndex &index, int role) const { if (mp_view->layer_model_updated ()) { lay::LayerPropertiesConstIterator iter (iterator (index)); if (iter.is_null () || iter.at_end ()) { return QVariant (); } else if (role == Qt::DisplayRole || role == Qt::EditRole) { if (index.column () == 1) { return QVariant (tl::to_qstring (iter->display_string (mp_view, true /*real*/))); } } else if (role == Qt::DecorationRole) { if (index.column () == 0) { bool animate_visible = true; unsigned int di_off = 0; if (iter->animation (true)) { if (iter->animation (true) == 1) { // scrolling di_off += m_phase; } else if (iter->animation (true) == 2) { // blinking animate_visible = ((m_phase & 1) == 0); } else { // inversly blinking animate_visible = ((m_phase & 1) != 0); } } unsigned int w = 32; unsigned int h = 16; if (animate_visible) { lay::color_t def_color = 0x808080; lay::color_t fill_color = iter->has_fill_color (true) ? iter->eff_fill_color (true) : def_color; lay::color_t frame_color = iter->has_frame_color (true) ? iter->eff_frame_color (true) : def_color; QImage image (w, h, QImage::Format_RGB32); image.fill (m_background_color.rgb ()); // TODO: adjust the resolution according to the oversampling mode lay::Bitmap fill (w, h, 1.0); lay::Bitmap frame (w, h, 1.0); lay::Bitmap text (w, h, 1.0); lay::Bitmap vertex (w, h, 1.0); unsigned int mask_w = 31; unsigned int mask_all = 0xfffffffe; unsigned int mask_left = 0x80000000; unsigned int mask_right = 0x00000002; unsigned int mask_center = 0x00010000; if (! iter->visible (true)) { mask_w = 8; mask_all = 0xff800000; mask_left = 0x80000000; mask_right = 0x00800000; mask_center = 0x08000000; // Show the arrow if it is invisible also locally. if (! iter->visible (false)) { text.scanline (4) [0] = 0x00008000 << 1; text.scanline (5) [0] = 0x00018000 << 1; text.scanline (6) [0] = 0x00038000 << 1; text.scanline (7) [0] = 0x00078000 << 1; text.scanline (8) [0] = 0x00038000 << 1; text.scanline (9) [0] = 0x00018000 << 1; text.scanline (10) [0] = 0x00008000 << 1; } } if (mp_view->no_stipples ()) { // Show a partial stipple pattern only for "no stipple" mode for (unsigned int i = 1; i < h - 2; ++i) { fill.scanline (i) [0] = 0xff800000; } } else { for (unsigned int i = 1; i < h - 2; ++i) { fill.scanline (i) [0] = mask_all; } } int lw = iter->width (true); if (lw < 0) { // default line width is 0 for parents and 1 for leafs lw = iter->has_children () ? 0 : 1; } int p0 = lw / 2; int p1 = (lw - 1) / 2; if (p0 < 0) { p0 = 0; } else if (p0 > 7) { p0 = 7; } if (p1 < 0) { p1 = 0; } else if (p1 > 7) { p1 = 7; } int p0x = p0, p1x = p1; unsigned int ddx = 0; unsigned int ddy = h - 2 - p1 - p0; if (iter->xfill (true)) { ddx = mask_w - p0 - p1 - 1; } unsigned int d = ddx / 2; frame.scanline (p0) [0] = mask_all << p1; for (unsigned int i = p0; i < h - 2; ++i) { frame.scanline (i) [0] |= (mask_left >> p0) | (mask_right << p1); frame.scanline (i) [0] |= (mask_left >> p0x) | (mask_right << p1x); while (d < ddx) { d += ddy; frame.scanline (i) [0] |= (mask_left >> p0x) | (mask_right << p1x); ++p0x; ++p1x; } if (d >= ddx) { d -= ddx; } } frame.scanline (h - 2 - p1) [0] = mask_all << p1; if (! iter->valid (true)) { text.scanline (4) [0] |= 0x00000c60; text.scanline (5) [0] |= 0x00000ee0; text.scanline (6) [0] |= 0x000007c0; text.scanline (7) [0] |= 0x00000380; text.scanline (8) [0] |= 0x000007c0; text.scanline (9) [0] |= 0x00000ee0; text.scanline (10) [0] |= 0x00000c60; for (unsigned int i = 3; i < 12; ++i) { fill.scanline (i) [0] &= ~0x00001ff0; frame.scanline (i) [0] &= ~0x00001ff0; } } vertex.scanline (h / 2 - 1) [0] = mask_center; lay::ViewOp::Mode mode = lay::ViewOp::Copy; // create fill single_bitmap_to_image (lay::ViewOp (fill_color, mode, 0, iter->eff_dither_pattern (true), di_off), fill, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); // create frame if (lw == 0) { single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0 /*solid line*/, 2 /*dotted*/, 0), frame, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); } else { single_bitmap_to_image (lay::ViewOp (frame_color, mode, iter->eff_line_style (true), 0, 0, lay::ViewOp::Rect, lw), frame, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); } // create text single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0), text, &image, mp_view->dither_pattern (), mp_view->line_styles (), w, h); // create vertex single_bitmap_to_image (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, iter->marked (true) ? 9/*mark size*/ : 0), vertex, &image, mp_view->dither_pattern (),mp_view->line_styles (), w, h); QPixmap pixmap = QPixmap::fromImage (image); // Qt 4.6.0 workaround return QVariant (QIcon (pixmap)); } else { return QVariant (QIcon ()); } } } else if (role == Qt::BackgroundRole) { if (m_selected_ids.find (size_t (index.internalPointer ())) != m_selected_ids.end ()) { // for selected items pick a color between Highlight and Base QPalette pl (mp_view->palette ()); QColor c1 = pl.color (QPalette::Highlight); QColor cb = pl.color (QPalette::Base); return QVariant (QColor ((c1.red () + cb.red ()) / 2, (c1.green () + cb.green ()) / 2, (c1.blue () + cb.blue ()) / 2)); } else { return QVariant (); } } else if (role == Qt::TextColorRole || role == Qt::FontRole) { if (index.column () == 1) { QColor c1 = m_text_color; QColor cb = m_background_color; QColor c0 = QColor ((c1.red () + cb.red ()) / 2, (c1.green () + cb.green ()) / 2, (c1.blue () + cb.blue ()) / 2); bool empty; if (m_test_shapes_in_view) { empty = empty_within_view_predicate (index); } else { empty = iter->bbox ().empty (); } // set the color to one with less contrast if there is nothing on this layer here if (role == Qt::FontRole) { QFont f = m_font; f.setBold (! empty); return QVariant (f); } else { return QVariant (empty ? c0 : c1); } } } } return QVariant (); } lay::LayerPropertiesConstIterator LayerTreeModel::iterator (const QModelIndex &index) const { if (index.isValid ()) { size_t iter_index = size_t (index.internalPointer ()); if (mp_view->layer_lists () > 0 && iter_index >= m_id_start && iter_index < m_id_end) { return lay::LayerPropertiesConstIterator (mp_view->get_properties (), iter_index - m_id_start); } } return lay::LayerPropertiesConstIterator (); } QModelIndex LayerTreeModel::index (lay::LayerPropertiesConstIterator iter, int column) const { try { std::vector rows; while (! iter.is_null ()) { rows.push_back (int (iter.child_index ())); iter = iter.parent (); } QModelIndex idx; for (std::vector::reverse_iterator r = rows.rbegin (); r != rows.rend (); ++r) { idx = index (*r, column, idx); } return idx; } catch (tl::Exception &ex) { tl::warn << ex.msg (); return QModelIndex (); } } } // namespace lay