mirror of https://github.com/KLayout/klayout.git
906 lines
21 KiB
C++
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
|
|
|
|
|