/* 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 "layBitmap.h" #include "layBitmapRenderer.h" #include "layFixedFont.h" #include "tlAlgorithm.h" namespace lay { Bitmap::Bitmap () : m_empty_scanline (0) { init (0, 0); m_resolution = 1.0; } Bitmap::Bitmap (unsigned int w, unsigned int h, double r) : m_empty_scanline (0) { init (w, h); m_resolution = r; } Bitmap::Bitmap (const Bitmap &d) : m_empty_scanline (0) { init (d.m_width, d.m_height); operator= (d); } Bitmap & Bitmap::operator= (const Bitmap &d) { if (&d != this) { if (m_width != d.m_width || m_height != d.m_height) { cleanup (); init (d.m_width, d.m_height); } m_resolution = d.m_resolution; for (unsigned int i = 0; i < m_height; ++i) { if (! d.m_scanlines.empty () && d.m_scanlines [i] != 0) { uint32_t *sl = scanline (i); uint32_t *ss = d.m_scanlines [i]; for (unsigned int b = (m_width + 31) / 32; b > 0; --b) { *sl++ = *ss++; } } else if (! m_scanlines.empty () && m_scanlines [i] != 0) { m_free.push_back (m_scanlines [i]); m_scanlines [i] = 0; } } m_last_sl = d.m_last_sl; m_first_sl = d.m_first_sl; } return *this; } Bitmap::~Bitmap () { cleanup (); } uint32_t * Bitmap::scanline (unsigned int n) { if (m_scanlines.empty ()) { m_scanlines.resize (m_height, 0); } uint32_t *sl = m_scanlines [n]; if (sl == 0) { unsigned int b = (m_width + 31) / 32; if (! m_free.empty ()) { sl = m_scanlines [n] = m_free.back (); m_free.pop_back (); } else { sl = m_scanlines [n] = new uint32_t [b]; } for (uint32_t *p = sl; b > 0; --b) { *p++ = 0; } if (m_first_sl > n) { m_first_sl = n; } if (m_last_sl <= n) { m_last_sl = n + 1; } } return sl; } void Bitmap::clear () { for (std::vector::iterator i = m_scanlines.begin (); i != m_scanlines.end (); ++i) { if (*i) { m_free.push_back (*i); } } for (std::vector::iterator i = m_scanlines.begin (); i != m_scanlines.end (); ++i) { *i = 0; } m_last_sl = m_first_sl = 0; } void Bitmap::cleanup () { m_last_sl = m_first_sl = 0; if (m_empty_scanline) { delete [] m_empty_scanline; m_empty_scanline = 0; } for (std::vector::iterator i = m_scanlines.begin (); i != m_scanlines.end (); ++i) { delete [] *i; } m_scanlines.clear (); for (std::vector::iterator i = m_free.begin (); i != m_free.end (); ++i) { delete [] *i; } m_free.clear (); m_width = m_height = 0; m_last_sl = m_first_sl = 0; } void Bitmap::init (unsigned int w, unsigned int h) { m_width = w; m_height = h; if (m_width > 0) { unsigned int b = (w + 31) / 32; m_empty_scanline = new uint32_t [b]; for (uint32_t *s = m_empty_scanline; b > 0; --b) { *s++ = 0; } } m_last_sl = m_first_sl = 0; } void Bitmap::merge (const lay::Bitmap *from, int dx, int dy) { if (! from) { return; } if (dx >= int (width ()) || dy >= int (height ())) { return; } unsigned int from_height = from->height (); if (int (from_height) + dy > int (height ())) { from_height = height () - dy; } unsigned int n0 = 0; if (dy < 0) { if (dy + int (from_height) <= 0) { return; } n0 = (unsigned int) -dy; } unsigned int from_width = from->width (); if (int (from_width) + dx > int (width ())) { from_width = width () - dx; } if (dx < 0) { if (dx + int (from_width) <= 0) { return; } unsigned int mo = ((unsigned int) -dx) / 32; unsigned int m = (from_width + 31) / 32 - mo; unsigned int mm = (from_width + dx + 31) / 32; unsigned int s1 = ((unsigned int) -dx) % 32; unsigned int s2 = 32 - s1; for (unsigned int n = n0; n < from_height; ++n) { if (from->is_scanline_empty (n)) { continue; } const uint32_t *sl_from = from->scanline (n) + mo; uint32_t *sl_to = scanline (n + dy); if (! s1) { for (unsigned int i = 0; i < m; ++i) { *sl_to++ |= *sl_from++; } } else if (m) { for (unsigned int i = 1; i < m; ++i) { *sl_to++ |= (sl_from[1] << s2) | (sl_from[0] >> s1); ++sl_from; } if (mm > m - 1) { *sl_to++ |= (sl_from[0] >> s1); } } } } else { unsigned int mo = ((unsigned int) dx) / 32; unsigned int m = (from_width + 31) / 32; unsigned int mm = (from_width + (((unsigned int) dx) % 32) + 31) / 32; unsigned int s1 = ((unsigned int) dx) % 32; unsigned int s2 = 32 - s1; for (unsigned int n = n0; n < from_height; ++n) { if (from->is_scanline_empty (n)) { continue; } const uint32_t *sl_from = from->scanline (n); uint32_t *sl_to = scanline (n + dy) + mo; if (! s1) { for (unsigned int i = 0; i < m; ++i) { *sl_to++ |= *sl_from++; } } else if (m) { *sl_to++ |= (sl_from[0] << s1); for (unsigned int i = 1; i < m; ++i) { *sl_to++ |= (sl_from[0] >> s2) | (sl_from[1] << s1); ++sl_from; } if (mm > m) { *sl_to++ |= (sl_from[0] >> s2); } } } } } void Bitmap::fill_pattern (int y, int x, const uint32_t *pp, unsigned int n) { if (x < int (m_width)) { if (y >= int (m_height)) { if (n <= (y - m_height + 1)) { return; } n -= y - m_height + 1; pp += y - m_height + 1; y = m_height - 1; } while (n > 0 && y >= 0) { uint32_t p = *pp; int x1 = x; if (x1 < 0) { if (x1 <= -32) { return; } p >>= (unsigned int)-x1; x1 = 0; } if (p) { unsigned int bx = ((unsigned int) x1) & ~(32 - 1); uint32_t *sl = scanline (y); sl += bx / 32; *sl |= (p << ((unsigned int)x1 - bx)); if ((unsigned int)x1 > bx) { bx += 32; ++sl; if (bx < m_width) { *sl |= (p >> (bx - (unsigned int)x1)); } } } ++pp; --n; --y; } } } static const uint32_t masks [32] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff }; static const uint32_t all_ones = 0xffffffff; void Bitmap::fill (unsigned int y, unsigned int x1, unsigned int x2) { unsigned int b1 = x1 / 32; uint32_t *sl = scanline (y); sl += b1; unsigned int b = x2 / 32 - b1; if (b == 0) { *sl |= (masks [x2 % 32] & ~masks [x1 % 32]); } else if (b > 0) { *sl++ |= ~masks [x1 % 32]; while (b > 1) { *sl++ |= all_ones; b--; } unsigned int m = masks [x2 % 32]; // Hint: if x2==width and width%32==0, sl must not be accessed. This is guaranteed by // checking if m != 0. if (m) { *sl |= m; } } } struct PosCompareF { bool operator() (const RenderEdge &a, const RenderEdge &b) const { return a.pos () < b.pos (); } }; struct X1CompareF { bool operator() (const RenderEdge &a, const RenderEdge &b) const { return a.x1 () < b.x1 (); } }; void Bitmap::render_fill (std::vector &edges) { // sort the edges so we can operate on the sorted list tl::sort (edges.begin (), edges.end ()); double y = std::max (0.0, floor (edges.begin ()->y1 ())); std::vector::iterator done = edges.begin (); // this is generic case while (done != edges.end () && y < height ()) { for ( ; done != edges.end (); ++done) { if (! done->done (y)) { break; } } std::vector::iterator todo = done; for ( ; todo != edges.end (); ++todo) { if (todo->done (y)) { std::swap (*done, *todo); ++done; } if (todo->todo (y)) { break; } } std::vector::iterator e; for (e = done; e != todo; ++e) { e->set_pos (e->x1 () + e->slope () * (y - e->y1 ())); } PosCompareF f; tl::sort (done, todo, f); int c = 0; bool x1set = false; double x1 = 0; unsigned int yint = (unsigned int) (y + 0.5); for (e = done; e != todo; ++e) { if (! e->is_horizontal ()) { c += e->delta (); if (c == 0) { // this is implementing the != 0 rule if (e->pos () > 0) { unsigned int x1int = 0; if (x1 > 0.0) { x1int = (unsigned int) x1; if (double (x1int) != x1) { ++x1int; } } fill (yint, x1int, (unsigned int) std::min (double (width () - 1), e->pos ()) + 1); } x1set = false; } else if (!x1set) { x1 = e->pos (); x1set = true; if (x1 >= double (width ())) { break; } } } } y += 1.0; } } void Bitmap::render_fill_ortho (std::vector &edges) { // sort the edges so we can operate on the sorted list tl::sort (edges.begin (), edges.end ()); double y = std::max (0.0, floor (edges.begin ()->y1 ())); std::vector::iterator done = edges.begin (); // this is the purely manhattan case // TODO: the manhattan optimization is not really effective .. while (done != edges.end () && y < height ()) { for ( ; done != edges.end (); ++done) { if (! done->done (y)) { break; } } std::vector::iterator todo = done; for ( ; todo != edges.end (); ++todo) { if (todo->done (y)) { std::swap (*done, *todo); ++done; } if (todo->todo (y)) { break; } } std::vector::iterator e; X1CompareF f; tl::sort (done, todo, f); int c = 0; bool x1set = false; double x1 = 0; unsigned int yint = (unsigned int) (y + 0.5); for (e = done; e != todo; ++e) { if (! e->is_horizontal ()) { c += e->delta (); if (c == 0) { // this is implementing the != 0 rule if (e->x1 () > 0) { unsigned int x1int = 0; if (x1 > 0.0) { x1int = (unsigned int) x1; if (double (x1int) != x1) { ++x1int; } } fill (yint, x1int, (unsigned int) std::min (double (width () - 1), e->x1 ()) + 1); } x1set = false; } else if (!x1set) { x1 = e->x1 (); x1set = true; if (x1 >= double (width ())) { break; } } } } y += 1.0; } } void Bitmap::render_vertices (std::vector &edges, int mode) { double xmax = width (); double ymax = height (); for (std::vector::iterator e = edges.begin (); e != edges.end (); ++e) { double x, y; if (mode == 0 || e->delta () > 0) { x = e->x1 () + 0.5; y = e->y1 () + 0.5; if (x >= 0.0 && x < xmax && y >= 0.0 && y < ymax) { unsigned int xint = (unsigned int)x; fill ((unsigned int)y, xint, xint + 1); } } if (mode == 0 || e->delta () < 0) { x = e->x2 () + 0.5; y = e->y2 () + 0.5; if (x >= 0.0 && x < xmax && y >= 0.0 && y < ymax) { unsigned int xint = (unsigned int)x; fill ((unsigned int)y, xint, xint + 1); } } if (mode == 2 && e != edges.end ()) { ++e; } } } void Bitmap::render_contour_ortho (std::vector &edges) { // this is the purely manhattan case for (std::vector::iterator e = edges.begin (); e != edges.end (); ++e) { // This is the line render algorithm // The basic idea is to decompose the line into stripes // associated with a integer y value. The stripe extends // from x1 to x2 then. The algorithm tries to find a // set of pixels on the y line that covers the range x1 // to x2 as good as possible and advances to the next y // value then. // TODO: the rendering would be somewhat more efficient if // we would first clip the line and then render it. This // way we could remove the tests in the rendering loop. if (! e->is_horizontal ()) { // vertical double x = e->x1 (); if (e->y1 () < double (height ()) - 0.5 && e->y2 () >= -0.5 && x < (double) width () - 0.5 && x >= -0.5) { unsigned int xint = (unsigned int) (std::max (0.0, std::min (double (width () - 1), x) + 0.5)); unsigned int yint = (unsigned int) std::max (floor (e->y1 () + 0.5), 0.0); unsigned int yeint = (unsigned int) std::min (double (height () - 1), std::max (floor (e->y2 () + 0.5), 0.0)); while (yint <= yeint) { fill (yint, xint, xint + 1); ++yint; } } } else { // horizontal double x1 = e->x1 (); double x2 = e->x2 (); if (x1 > x2) { std::swap (x1, x2); } double y = e->y1 (); if (y < (double) height () - 0.5 && y >= -0.5 && x1 < (double) width () - 0.5 && x2 >= -0.5) { unsigned int x1int = (unsigned int) (std::max (0.0, std::min (double (width () - 1), x1) + 0.5)); unsigned int x2int = (unsigned int) (std::max (0.0, std::min (double (width () - 1), x2) + 0.5)); unsigned int yint = (unsigned int) std::max (floor (y + 0.5), 0.0); fill (yint, x1int, x2int + 1); } } } } void Bitmap::render_contour (std::vector &edges) { // this is the generic case for (std::vector::iterator e = edges.begin (); e != edges.end (); ++e) { // This is the line render algorithm // The basic idea is to decompose the line into stripes // associated with a integer y value. The stripe extends // from x1 to x2 then. The algorithm tries to find a // set of pixels on the y line that covers the range x1 // to x2 as good as possible and advances to the next y // value then. // TODO: the rendering would be somewhat more efficient if // we would first clip the line and then render it. This // way we could remove the tests in the rendering loop. if (e->y1 () < double (height ()) - 0.5 && e->y2 () >= -0.5) { double y = std::max (floor (e->y1 () + 0.5), 0.0); double x = e->pos (y - 0.5); double dx = e->pos (y + 0.5) - x; double dx1 = (e->y2 () - e->y1 ()) < 1e-6 ? 0.0 : (e->x2 () - e->x1 ()) / (e->y2 () - e->y1 ()); double y2m = e->y2 () - 0.5; unsigned int yeint = (unsigned int) std::min (double (height () - 1), std::max (floor (e->y2 () + 0.5), 0.0)); unsigned int xint = (unsigned int) (std::max (0.0, std::min (double (width () - 1), x) + 0.5)); unsigned int yint = (unsigned int) y; if (x < (double) width () - 0.5 && x >= 0.0) { fill (yint, xint, xint + 1); } if (e->x2 () > e->x1 ()) { while (yint <= yeint) { double xx; if (double (yint) > y2m) { xx = e->x2 () + 0.5; } else { xx = x + dx; dx = dx1; } unsigned int xe; if (xx >= 0.0) { if (xx >= (double) width ()) { if (x >= (double) width () - 1) { break; // done. } xe = width () - 1; } else { xe = (unsigned int) (xx); } if (xe <= xint) { fill (yint, xint, xint + 1); } else { fill (yint, xint + 1, xe + 1); xint = xe; } } else { xint = 0; } x = xx; ++yint; } } else { while (yint <= yeint) { double xx; if (double (yint) > y2m) { xx = e->x2 () - 0.5; } else { xx = x + dx; dx = dx1; } unsigned int xe; if (xx < double (width () - 1)) { if (xx < 0.0) { if (x <= 0.0) { break; } xe = 0; } else { xe = (unsigned int) (xx); if (double (xe) != xx) { ++xe; } } if (xe >= xint) { fill (yint, xint, xint + 1); } else { fill (yint, xe, xint); xint = xe; } } else { xint = width (); } x = xx; ++yint; } } } } } static unsigned char next_char_latin1_from_utf8 (const char *&cp, const char *cpf = 0) { unsigned char c = *cp; if ((c & 0xe0) == 0xc0) { if ((cp[1] & 0xc0) == 0x80 && (! cpf || cpf > cp + 1)) { unsigned int x = ((unsigned int) ((unsigned char) c & 0x1f) << 6) | (unsigned int) (cp[1] & 0x3f); cp += 1; if (x < 255) { c = x; } else { c = '?'; } } else { c = '?'; } } else if ((c & 0xf0) == 0xe0) { if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (! cpf || cpf > cp + 2)) { cp += 2; } c = '?'; } else if ((c & 0xf8) == 0xf0) { if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (cp[3] & 0xc0) == 0x80 && (! cpf || cpf > cp + 3)) { cp += 3; } c = '?'; } return c; } void Bitmap::render_text (const lay::RenderText &text) { if (text.font == db::DefaultFont) { const lay::FixedFont &ff = lay::FixedFont::get_font (m_resolution); // count the lines and max. characters per line unsigned int lines = 1; for (const char *cp = text.text.c_str (); *cp; ++cp) { if (*cp == '\012' || *cp == '\015') { if (*cp == '\015' && cp[1] == '\012') { ++cp; } ++lines; } } // compute the actual top left position double y; if (text.valign == db::VAlignBottom || text.valign == db::NoVAlign) { y = text.b.bottom (); y += double (ff.line_height () * (lines - 1) + ff.height ()); } else if (text.valign == db::VAlignCenter) { y = text.b.center ().y (); y += double ((ff.line_height () * (lines - 1) + ff.height ()) / 2); } else { y = text.b.top (); } // start generating the characters const char *cp1 = text.text.c_str (); while (*cp1) { unsigned int length = 0; const char *cp = cp1; while (*cp && *cp != '\012' && *cp != '\015') { next_char_latin1_from_utf8 (cp); ++length; ++cp; } double xx; if (text.halign == db::HAlignRight) { xx = text.b.right (); xx -= double (ff.width () * length); } else if (text.halign == db::HAlignCenter) { xx = text.b.center ().x (); xx -= double (ff.width () * length / 2); } else { xx = text.b.left (); } xx -= 0.5; if (y > -0.5 && y < double (height () + ff.height () - 1) - 0.5) { for ( ; cp1 != cp; ++cp1) { unsigned char c = next_char_latin1_from_utf8 (cp1, cp); size_t cc = c; // to suppress a compiler warning .. if (c >= ff.first_char () && cc < size_t (ff.n_chars ()) + size_t (ff.first_char ()) && xx > -100.0 && xx < double (width ())) { fill_pattern (int (y + 0.5), int (floor (xx)), ff.data () + (c - ff.first_char ()) * ff.height (), ff.height ()); } xx += double (ff.width ()); } } else { cp1 = cp; } // next line if (*cp1 == '\012' || *cp1 == '\015') { if (*cp1 == '\015' && cp1[1] == '\012') { ++cp1; } ++cp1; y -= double (ff.line_height ()); } } } else { // Create a sub-renderer so we do not need to clear *this lay::BitmapRenderer hr (m_width, m_height, m_resolution); db::DHershey ht (text.text, text.font); hr.reserve_edges (ht.count_edges ()); ht.justify (text.b.transformed (text.trans.inverted ()), text.halign, text.valign); // text becomes unreadable at a low scaling factor - don't draw then. if (ht.scale_factor () > 0.2) { db::DHershey::edge_iterator e = ht.begin_edges (); while (! e.at_end ()) { hr.insert ((*e).transformed (text.trans)); ++e; } } hr.render_contour (*this); } } } // namespace lay