klayout/src/laybasic/layBitmap.cc

906 lines
21 KiB
C++

/*
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<uint32_t *>::iterator i = m_scanlines.begin (); i != m_scanlines.end (); ++i) {
if (*i) {
m_free.push_back (*i);
}
}
for (std::vector<uint32_t *>::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<uint32_t *>::iterator i = m_scanlines.begin (); i != m_scanlines.end (); ++i) {
delete [] *i;
}
m_scanlines.clear ();
for (std::vector<uint32_t *>::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<lay::RenderEdge> &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<lay::RenderEdge>::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<lay::RenderEdge>::iterator todo = done;
for ( ; todo != edges.end (); ++todo) {
if (todo->done (y)) {
std::swap (*done, *todo);
++done;
}
if (todo->todo (y)) {
break;
}
}
std::vector<lay::RenderEdge>::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<lay::RenderEdge> &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<lay::RenderEdge>::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<lay::RenderEdge>::iterator todo = done;
for ( ; todo != edges.end (); ++todo) {
if (todo->done (y)) {
std::swap (*done, *todo);
++done;
}
if (todo->todo (y)) {
break;
}
}
std::vector<lay::RenderEdge>::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<lay::RenderEdge> &edges, int mode)
{
double xmax = width ();
double ymax = height ();
for (std::vector<lay::RenderEdge>::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<lay::RenderEdge> &edges)
{
// this is the purely manhattan case
for (std::vector<lay::RenderEdge>::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<lay::RenderEdge> &edges)
{
// this is the generic case
for (std::vector<lay::RenderEdge>::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