/* * Copyright (c) 1987, 1988, 1989, 1990, 1991 Stanford University * Copyright (c) 1991 Silicon Graphics, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that (i) the above copyright notices and this permission notice appear in * all copies of the software and related documentation, and (ii) the names of * Stanford and Silicon Graphics may not be used in any advertising or * publicity relating to the software without the specific, prior written * permission of Stanford and Silicon Graphics. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __DECCXX struct _XRegion; #endif declarePtrList(TransformerStack,Transformer) implementPtrList(TransformerStack,Transformer) declarePtrList(ClippingStack,_XRegion) implementPtrList(ClippingStack,_XRegion) /* class Canvas */ Canvas::Canvas() { CanvasRep* c = new CanvasRep; rep_ = c; TextRenderInfo* t = &CanvasRep::text_; PathRenderInfo* p = &CanvasRep::path_; if (t->text_ == nil) { t->text_ = new char[1000]; t->cur_text_ = t->text_; t->items_ = new XTextItem[100]; } if (p->point_ == nil) { p->point_ = new XPoint[10]; p->cur_point_ = p->point_; p->end_point_ = p->point_ + 10; } c->drawbuffer_ = CanvasRep::unbound; c->copybuffer_ = CanvasRep::unbound; c->drawgc_ = nil; c->copygc_ = nil; c->brush_ = nil; c->brush_width_ = 0; c->dash_list_ = nil; c->dash_count_ = 0; c->color_ = nil; c->pixel_ = 0; c->stipple_ = 0; c->font_ = nil; c->text_twobyte_ = false; c->text_reencode_ = false; c->clipping_ = XCreateRegion(); c->empty_ = XCreateRegion(); c->transformers_ = new TransformerStack; c->clippers_ = new ClippingStack; Transformer* identity = new Transformer; c->transformers_->append(identity); c->transformed_ = false; c->display_ = nil; c->window_ = nil; c->xdrawable_ = CanvasRep::unbound; c->width_ = 0; c->height_ = 0; c->pwidth_ = 0; c->pheight_ = 0; c->damaged_ = false; c->on_damage_list_ = false; c->repairing_ = false; c->status_ = unmapped; } Canvas::~Canvas() { CanvasRep* c = rep(); c->unbind(); for (ListItr(TransformerStack) i(*c->transformers_); i.more(); i.next()) { Transformer* t = i.cur(); delete t; } delete c->transformers_; XDestroyRegion(c->clipping_); XDestroyRegion(c->empty_); delete c->clippers_; delete c; rep_ = nil; } void Canvas::size(Coord width, Coord height) { CanvasRep* c = rep(); c->width_ = width; c->height_ = height; Display* d = c->display_; if (d != nil) { c->pwidth_ = d->to_pixels(width); c->pheight_ = d->to_pixels(height); } } void Canvas::psize(PixelCoord pwidth, PixelCoord pheight) { CanvasRep& c = *rep(); c.pwidth_ = pwidth; c.pheight_ = pheight; Display* d = c.display_; if (d != nil) { c.width_ = d->to_coord(pwidth); c.height_ = d->to_coord(pheight); } } Coord Canvas::width() const { return rep()->width_; } Coord Canvas::height() const { return rep()->height_; } PixelCoord Canvas::pwidth() const { return rep()->pwidth_; } PixelCoord Canvas::pheight() const { return rep()->pheight_; } PixelCoord Canvas::to_pixels(Coord p) const { return rep()->display_->to_pixels(p); } Coord Canvas::to_coord(PixelCoord p) const { return rep()->display_->to_coord(p); } Coord Canvas::to_pixels_coord(Coord p) const { const Display& d = *rep()->display_; return d.to_coord(d.to_pixels(p)); } void Canvas::push_transform() { CanvasRep* c = rep(); c->flush(); TransformerStack& s = *c->transformers_; Transformer* m = new Transformer(*s.item(s.count() - 1)); s.append(m); } void Canvas::pop_transform() { CanvasRep* c = rep(); c->flush(); TransformerStack& s = *c->transformers_; long i = s.count() - 1; if (i == 0) { /* * We pushed the first matrix during initialization, * so we must be underflowing the stack. Should be an exception. */ return; } Transformer* m = s.item(i); delete m; s.remove(i); c->transformed_ = !c->matrix().identity(); } void Canvas::transform(const Transformer& t) { CanvasRep* c = rep(); c->flush(); c->matrix().premultiply(t); c->transformed_ = !c->matrix().identity(); } void Canvas::transformer(const Transformer& t) { CanvasRep* c = rep(); c->flush(); c->matrix() = t; c->transformed_ = !t.identity(); } const Transformer& Canvas::transformer() const { CanvasRep* c = rep(); return c->matrix(); } /* * There doesn't appear to be any way to copy a region other than * to union it with an empty region. */ void Canvas::push_clipping() { CanvasRep* c = rep(); c->flush(); Region old_clip = c->clipping_; Region new_clip = XCreateRegion(); XUnionRegion(old_clip, c->empty_, new_clip); c->clippers_->append(old_clip); c->clipping_ = new_clip; } void Canvas::pop_clipping() { CanvasRep* c = rep(); c->flush(); ClippingStack& s = *c->clippers_; long n = s.count(); if (n == 0) { /* stack underflow--should raise exception */ return; } Region clip = c->clipping_; XDestroyRegion(clip); clip = s.item(n - 1); s.remove(n - 1); c->clipping_ = clip; XDisplay* dpy = c->display_->rep()->display_; GC gc = c->drawgc_; if (XEmptyRegion(clip)) { XSetClipMask(dpy, gc, None); } else { XSetRegion(dpy, gc, clip); } } void Canvas::front_buffer() { CanvasRep& c = *rep(); if (c.copybuffer_ != CanvasRep::unbound) { c.drawbuffer_ = c.copybuffer_; } } void Canvas::back_buffer() { CanvasRep& c = *rep(); if (c.copybuffer_ != CanvasRep::unbound) { c.drawbuffer_ = c.xdrawable_; } } static boolean rectangular( int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3 ) { return ( (x0 == x1 && y1 == y2 && x2 == x3 && y3 == y0) || (x0 == x3 && y3 == y2 && x2 == x1 && y1 == y0) ); } static boolean xrect(const XPoint* p, unsigned int n) { return ( n == 5 && p[0].x == p[4].x && p[0].y == p[4].y && rectangular( p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y ) ); } static const float smoothness = 10.0; static boolean straight( const Transformer& tx, Coord x0, Coord y0, Coord x1, Coord y1, Coord x2, Coord y2, Coord x3, Coord y3 ) { Coord tx0, tx1, tx2, tx3; Coord ty0, ty1, ty2, ty3; tx.transform(x0, y0, tx0, ty0); tx.transform(x1, y1, tx1, ty1); tx.transform(x2, y2, tx2, ty2); tx.transform(x3, y3, tx3, ty3); float f = ( (tx1 + tx2) * (ty0 - ty3) + (ty1 + ty2) * (tx3 - tx0) + 2 * (tx0 * ty3 - ty0 * tx3) ); return (f * f) < smoothness; } static inline Coord mid(Coord a, Coord b) { return (a + b) / 2; } static char _txkey (int i) { if (i >= 0) { return ( i<32 ? i : i<160 ? 24 + (i>>2) : i<672 ? 54 + (i>>4) : 127 ); } else { return ( i>-32 ? i : i>-160 ? -24 - (i>>2) : i>-672 ? -54 - (i>>4) : -127 ); } } static int tx_key(const Transformer& tx, Coord x, Coord y) { Coord x1, y1, x2, y2, x3, y3; tx.transform(0, 0, x1, y1); tx.transform(0, y, x2, y2); tx.transform(x, 0, x3, y3); int k1 = _txkey(int(x2 - x1)) & 0xff; int k2 = _txkey(int(y2 - y1 - y)) & 0xff; int k3 = _txkey(int(x3 - x1 - x)) & 0xff; int k4 = _txkey(int(y3 - y1)) & 0xff; return (k1 << 24) + (k2 << 16) + (k3 << 8) + k4; } void Canvas::new_path() { PathRenderInfo* p = &CanvasRep::path_; p->curx_ = 0; p->cury_ = 0; XPoint* xp = p->point_; xp->x = 0; xp->y = 0; p->cur_point_ = xp; } void Canvas::move_to(Coord x, Coord y) { CanvasRep* c = rep(); PathRenderInfo* p = &CanvasRep::path_; p->curx_ = x; p->cury_ = y; Coord tx, ty; if (c->transformed_) { c->matrix().transform(x, y, tx, ty); } else { tx = x; ty = y; } Display* d = c->display_; XPoint* xp = p->point_; xp->x = d->to_pixels(tx); xp->y = c->pheight_ - d->to_pixels(ty); p->cur_point_ = xp + 1; } static XPoint* next_point(PathRenderInfo* p) { if (p->cur_point_ == p->end_point_) { int old_size = p->cur_point_ - p->point_; int new_size = 2 * old_size; XPoint* new_path = new XPoint[new_size]; for (int i = 0; i < old_size; i++) { new_path[i] = p->point_[i]; } delete p->point_; p->point_ = new_path; p->cur_point_ = p->point_ + old_size; p->end_point_ = p->point_ + new_size; } XPoint* xp = p->cur_point_; p->cur_point_ = xp + 1; return xp; } void Canvas::line_to(Coord x, Coord y) { CanvasRep* c = rep(); PathRenderInfo* p = &CanvasRep::path_; p->curx_ = x; p->cury_ = y; Coord tx, ty; if (c->transformed_) { c->matrix().transform(x, y, tx, ty); } else { tx = x; ty = y; } Display* d = c->display_; XPoint* xp = next_point(p); xp->x = d->to_pixels(tx); xp->y = c->pheight_ - d->to_pixels(ty); } void Canvas::curve_to( Coord x, Coord y, Coord x1, Coord y1, Coord x2, Coord y2 ) { CanvasRep* c = rep(); PathRenderInfo* p = &CanvasRep::path_; Coord px = p->curx_; Coord py = p->cury_; if (straight(c->matrix(), px, py, x1, y1, x2, y2, x, y)) { line_to(x, y); } else { Coord xx = mid(x1, x2); Coord yy = mid(y1, y2); Coord x11 = mid(px, x1); Coord y11 = mid(py, y1); Coord x22 = mid(x2, x); Coord y22 = mid(y2, y); Coord x12 = mid(x11, xx); Coord y12 = mid(y11, yy); Coord x21 = mid(xx, x22); Coord y21 = mid(yy, y22); Coord cx = mid(x12, x21); Coord cy = mid(y12, y21); curve_to(cx, cy, x11, y11, x12, y12); curve_to(x, y, x21, y21, x22, y22); } } void Canvas::close_path() { PathRenderInfo* p = &CanvasRep::path_; XPoint* startp = p->point_; XPoint* xp = next_point(p); xp->x = startp->x; xp->y = startp->y; } void Canvas::stroke(const Color* color, const Brush* b) { CanvasRep* c = rep(); PathRenderInfo* p = &CanvasRep::path_; int n = p->cur_point_ - p->point_; if (n < 2) { return; } c->flush(); c->color(color); c->brush(b); XDisplay* dpy = c->display_->rep()->display_; XDrawable d = c->drawbuffer_; GC gc = c->drawgc_; XPoint* pt = p->point_; if (n == 2) { XDrawLine(dpy, d, gc, pt[0].x, pt[0].y, pt[1].x, pt[1].y); } else if (xrect(pt, n)) { int x = Math::min(pt[0].x, pt[2].x); int y = Math::min(pt[0].y, pt[2].y); int w = Math::abs(pt[0].x - pt[2].x); int h = Math::abs(pt[0].y - pt[2].y); XDrawRectangle(dpy, d, gc, x, y, w, h); } else { XDrawLines(dpy, d, gc, pt, n, CoordModeOrigin); } } void Canvas::line( Coord x1, Coord y1, Coord x2, Coord y2, const Color* c, const Brush* b ) { new_path(); move_to(x1, y1); line_to(x2, y2); stroke(c, b); } static void rect_path(Canvas* c, Coord l, Coord b, Coord r, Coord t) { c->new_path(); c->move_to(l, b); c->line_to(l, t); c->line_to(r, t); c->line_to(r, b); c->close_path(); } void Canvas::rect( Coord l, Coord b, Coord r, Coord t, const Color* c, const Brush* br ) { rect_path(this, l, b, r, t); stroke(c, br); } void Canvas::fill(const Color* color) { CanvasRep* c = rep(); PathRenderInfo* p = &CanvasRep::path_; int n = p->cur_point_ - p->point_; if (n <= 2) { return; } c->flush(); c->color(color); XDisplay* dpy = c->display_->rep()->display_; XDrawable d = c->drawbuffer_; GC gc = c->drawgc_; XPoint* pt = p->point_; if (xrect(pt, n)) { int x = Math::min(pt[0].x, pt[2].x); int y = Math::min(pt[0].y, pt[2].y); int w = Math::abs(pt[0].x - pt[2].x); int h = Math::abs(pt[0].y - pt[2].y); XFillRectangle(dpy, d, gc, x, y, w, h); } else { XFillPolygon(dpy, d, gc, pt, n, Complex, CoordModeOrigin); } } void Canvas::fill_rect(Coord l, Coord b, Coord r, Coord t, const Color* c) { rect_path(this, l, b, r, t); fill(c); } void Canvas::clip() { CanvasRep* c = rep(); c->flush(); PathRenderInfo* p = &CanvasRep::path_; int n = p->cur_point_ - p->point_; if (n <= 2) { return; } Region clip; XPoint* pt = p->point_; if (xrect(pt, n)) { XRectangle xr; xr.x = Math::min(pt[0].x, pt[2].x); xr.y = Math::min(pt[0].y, pt[2].y); xr.width = Math::abs(pt[0].x - pt[2].x); xr.height = Math::abs(pt[0].y - pt[2].y); clip = XCreateRegion(); XUnionRectWithRegion(&xr, c->empty_, clip); } else { clip = XPolygonRegion(pt, n, EvenOddRule); } if (!XEmptyRegion(c->clipping_)) { Region intersect = XCreateRegion(); XIntersectRegion(c->clipping_, clip, intersect); XDestroyRegion(clip); clip = intersect; } XDestroyRegion(c->clipping_); c->clipping_ = clip; XSetRegion(c->dpy(), c->drawgc_, clip); } void Canvas::clip_rect(Coord l, Coord b, Coord r, Coord t) { rect_path(this, l, b, r, t); clip(); } static const int adobe_to_iso8859[] = { 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, 0240, 0241, 0242, 0243, 0057, 0245, 0000, 0247, 0244, 0264, 0042, 0253, 0000, 0000, 0000, 0000, 0000, 0055, 0000, 0000, 0267, 0000, 0266, 0052, 0000, 0000, 0042, 0273, 0137, 0000, 0000, 0277, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0055, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000 }; declareTable2(CharBitmapTable,const Font*,long,const Bitmap*) implementTable2(CharBitmapTable,const Font*,long,const Bitmap*) static CharBitmapTable* _char_bitmaps; static const Bitmap* char_bitmap(Display* d, const Font* font, long c) { if (_char_bitmaps == nil) { _char_bitmaps = new CharBitmapTable(1024); } const Bitmap* b; if (!_char_bitmaps->find(b, font, c)) { b = new Bitmap(font, c, font->rep(d)->scale_); _char_bitmaps->insert(font, c, b); } return b; } void Canvas::character( const Font* f, long ch, Coord width, const Color* color, Coord x, Coord y ) { CanvasRep* c = rep(); int int_ch = int(ch); boolean is_flush = !isprint(int_ch); if (f != nil && f != c->font_) { c->flush(); c->font(f); } if (color != nil && color != c->color_) { c->flush(); c->color(color); } Transformer& m = c->matrix(); if (!c->font_is_scaled_ && (!c->transformed_ || tx_key(m, width, width) == 0) ) { TextRenderInfo* tr = &CanvasRep::text_; if (is_flush || y != tr->cury_ || !Math::equal(x, tr->curx_, float(0.1)) ) { c->flush(); } char* cp = tr->cur_text_; if (cp == tr->text_) { Coord tx = x; Coord ty = y; if (c->transformed_) { m.transform(tx, ty); } tr->canvas_ = c; tr->drawgc_ = c->drawgc_; Display* d = c->display_; tr->x0_ = d->to_pixels(tx); tr->y0_ = c->pheight_ - d->to_pixels(ty); tr->width_ = 0; } tr->width_ += width; tr->curx_ = x + width; tr->cury_ = y; if (c->text_twobyte_) { *cp++ = char((ch & 0xff00) >> 8); *cp++ = char(ch & 0xff); } else if (c->text_reencode_) { *cp++ = adobe_to_iso8859[ch & 0xff]; } else { *cp++ = char(ch & 0xff); } tr->cur_text_ = cp; if (ch == ' ') { tr->spaces_ += 1; if (cp > tr->text_ + 1) { c->flush(); } } if (is_flush) { c->flush(); } } else if (ch != ' ') { c->flush(); stencil(char_bitmap(c->display_, f, ch), color, x, y); } } declareTable2(TxBitmapTable,const Bitmap*,int,BitmapRep*) implementTable2(TxBitmapTable,const Bitmap*,int,BitmapRep*) static TxBitmapTable* _tx_bitmaps; static BitmapRep* tx_bitmap(const Bitmap* b, const Transformer& tx) { int key = tx_key(tx, b->width(), b->height()); if (key == 0) { return b->rep(); } else { if (_tx_bitmaps == nil) { _tx_bitmaps = new TxBitmapTable(1024); } BitmapRep* rep; if (!_tx_bitmaps->find(rep, b, key)) { Display* d = b->rep()->display_; rep = new BitmapRep; Transformer v(tx); Coord x0, y0; v.transform(0, 0, x0, y0); v.translate(-x0, -y0); Coord x1, x2, x3, x4; Coord y1, y2, y3, y4; v.transform(-b->left_bearing(), -b->descent(), x1, y1); v.transform(-b->left_bearing(), b->ascent(), x2, y2); v.transform(b->right_bearing(), b->ascent(), x3, y3); v.transform(b->right_bearing(), -b->descent(), x4, y4); Coord xmax = Math::max(x1, x2, x3, x4); Coord xmin = Math::min(x1, x2, x3, x4); Coord ymax = Math::max(y1, y2, y3, y4); Coord ymin = Math::min(y1, y2, y3, y4); int width = d->to_pixels(xmax) - d->to_pixels(xmin); int height = d->to_pixels(ymax) - d->to_pixels(ymin); if (width <= 0) { width = 1; } if (height <= 0) { height = 1; } XDisplay* dpy = d->rep()->display_; BitmapRep* srep = b->rep(); XImage* source = XGetImage( dpy, srep->pixmap_, 0, 0, srep->pwidth_, srep->pheight_, 0x01, ZPixmap ); Pixmap map = XCreatePixmap(dpy, d->rep()->root_, width, height, 1); GC xgc = XCreateGC(dpy, map, 0, nil); XSetForeground(dpy, xgc, 0); XFillRectangle(dpy, map, xgc, 0, 0, width, height); XImage* dest = XGetImage( dpy, map, 0, 0, width, height, 0x01, ZPixmap ); int dx0 = d->to_pixels(-xmin); int dy0 = d->to_pixels(-ymin); int sx0 = d->to_pixels(b->left_bearing()); int sy0 = d->to_pixels(b->descent()); for (int dy = 0; dy < height; ++dy) { Coord tx1, ty1, tx2, ty2; v.inverse_transform(- dx0, dy - dy0, tx1, ty1); v.inverse_transform(width - dx0, dy - dy0, tx2, ty2); float delta_x = (tx2 - tx1) / width; float delta_y = (ty2 - ty1) / width; int sx, sy; for (int dx = 0; dx < width; ++dx) { sx = int(tx1) + sx0; sy = int(ty1) + sy0; if ( sx >= 0 && sx < srep->pwidth_ && sy >= 0 && sy < srep->pheight_ ) { XPutPixel( dest, dx, height - 1 - dy, XGetPixel(source, sx, srep->pheight_ - 1 - sy) ); } tx1 = tx1 + delta_x; ty1 = ty1 + delta_y; } } XPutImage(dpy, map, xgc, dest, 0, 0, 0, 0, width, height); XFreeGC(dpy, xgc); XDestroyImage(source); XDestroyImage(dest); rep->display_ = d; rep->pixmap_ = map; rep->pwidth_ = width; rep->pheight_ = height; rep->width_ = xmax - xmin; rep->height_ = ymax - ymin; rep->left_ = xmin; rep->right_ = xmax; rep->bottom_ = ymin; rep->top_ = ymax; _tx_bitmaps->insert(b, key, rep); } return rep; } } void Canvas::stencil( const Bitmap* mask, const Color* color, Coord x, Coord y ) { CanvasRep& c = *rep(); c.flush(); XDisplay* dpy = c.dpy(); XDrawable d = c.drawbuffer_; Transformer& m = c.matrix(); mask->flush(); BitmapRep* info = tx_bitmap(mask, m); Coord tx, ty; if (c.transformed_) { m.transform(x, y, tx, ty); } else { tx = x; ty = y; } Display* disp = c.display_; int pleft = disp->to_pixels(tx + info->left_); int ptop = c.pheight_ - disp->to_pixels(ty + info->top_); XGCValues gcv; unsigned long valuemask = 0; valuemask |= GCFunction; gcv.function = GXand; valuemask |= GCForeground; gcv.foreground = 0; valuemask |= GCBackground; gcv.background = AllPlanes; valuemask |= GCGraphicsExposures; gcv.graphics_exposures = False; GC xgc = XCreateGC(dpy, d, valuemask, &gcv); XCopyGC(dpy, c.drawgc_, GCClipMask, xgc); XCopyPlane( dpy, info->pixmap_, d, xgc, 0, 0, info->pwidth_, info->pheight_, pleft, ptop, 1 ); gcv.function = GXxor; gcv.foreground = color->rep(c.window_->rep()->visual_)->xcolor_.pixel; gcv.background = 0; valuemask &= ~GCGraphicsExposures; XChangeGC(dpy, xgc, valuemask, &gcv); XCopyPlane( dpy, info->pixmap_, d, xgc, 0, 0, info->pwidth_, info->pheight_, pleft, ptop, 1 ); XFreeGC(dpy, xgc); } declareTable2(TxRasterTable,const Raster*,int,RasterRep*) implementTable2(TxRasterTable,const Raster*,int,RasterRep*) static TxRasterTable* _tx_rasters; static RasterRep* tx_raster(const Raster* r, const Transformer& tx) { int key = tx_key(tx, r->width(), r->height()); if (key == 0) { return r->rep(); } else { if (_tx_rasters == nil) { _tx_rasters = new TxRasterTable(1024); } RasterRep* rep; if (!_tx_rasters->find(rep, r, key)) { Display* d = r->rep()->display_; DisplayRep& dr = *d->rep(); rep = new RasterRep; Transformer v(tx); Coord x0, y0; v.transform(0, 0, x0, y0); v.translate(-x0, -y0); Coord x1, x2, x3, x4; Coord y1, y2, y3, y4; v.transform(-r->left_bearing(), -r->descent(), x1, y1); v.transform(-r->left_bearing(), r->ascent(), x2, y2); v.transform(r->right_bearing(), r->ascent(), x3, y3); v.transform(r->right_bearing(), -r->descent(), x4, y4); Coord xmax = Math::max(x1, x2, x3, x4); Coord xmin = Math::min(x1, x2, x3, x4); Coord ymax = Math::max(y1, y2, y3, y4); Coord ymin = Math::min(y1, y2, y3, y4); int width = d->to_pixels(xmax) - d->to_pixels(xmin); int height = d->to_pixels(ymax) - d->to_pixels(ymin); if (width <= 0) { width = 1; } if (height <= 0) { height = 1; } XDisplay* dpy = dr.display_; RasterRep* srep = r->rep(); XImage* source = XGetImage( dpy, srep->pixmap_, 0, 0, srep->pwidth_, srep->pheight_, AllPlanes, ZPixmap ); Pixmap map = XCreatePixmap( dpy, dr.root_, width, height, dr.default_visual_->depth() ); GC xgc = XCreateGC(dpy, map, 0, nil); XSetForeground(dpy, xgc, 0); XFillRectangle(dpy, map, xgc, 0, 0, width, height); XImage* dest = XGetImage( dpy, map, 0, 0, width, height, AllPlanes, ZPixmap ); int dx0 = d->to_pixels(-xmin); int dy0 = d->to_pixels(-ymin); int sx0 = d->to_pixels(r->left_bearing()); int sy0 = d->to_pixels(r->descent()); for (int dy = 0; dy < height; ++dy) { Coord tx1, ty1, tx2, ty2; v.inverse_transform(- dx0, dy - dy0, tx1, ty1); v.inverse_transform(width - dx0, dy - dy0, tx2, ty2); float delta_x = (tx2 - tx1) / width; float delta_y = (ty2 - ty1) / width; int sx, sy; for (int dx = 0; dx < width; ++dx) { sx = int(tx1) + sx0; sy = int(ty1) + sy0; if ( sx >= 0 && sx < srep->pwidth_ && sy >= 0 && sy < srep->pheight_ ) { XPutPixel( dest, dx, height - 1 - dy, XGetPixel(source, sx, srep->pheight_ - 1 - sy) ); } tx1 = tx1 + delta_x; ty1 = ty1 + delta_y; } } XPutImage(dpy, map, xgc, dest, 0, 0, 0, 0, width, height); XFreeGC(dpy, xgc); XDestroyImage(source); XDestroyImage(dest); rep->display_ = d; rep->pixmap_ = map; rep->pwidth_ = width; rep->pheight_ = height; rep->width_ = xmax - xmin; rep->height_ = ymax - ymin; rep->left_ = xmin; rep->right_ = xmax; rep->bottom_ = ymin; rep->top_ = ymax; _tx_rasters->insert(r, key, rep); } return rep; } } void Canvas::image(const Raster* image, Coord x, Coord y) { CanvasRep* c = rep(); c->flush(); XDisplay* dpy = c->dpy(); GC gc = c->drawgc_; Transformer& m = c->matrix(); image->flush(); RasterRep* info = tx_raster(image, m); Coord tx, ty; if (c->transformed_) { m.transform(x, y, tx, ty); } else { tx = x; ty = y; } Display* d = c->display_; int pleft = d->to_pixels(tx + info->left_); int ptop = c->pheight_ - d->to_pixels(ty + info->top_); /* * We assume that graphics exposures are off in the gc. */ XCopyArea( dpy, info->pixmap_, c->drawbuffer_, gc, 0, 0, info->pwidth_, info->pheight_, pleft, ptop ); } Window* Canvas::window() const { return rep()->window_; } /* * Damage coordinates are always absolute with respect to the canvas, * so they are not transformed. */ void Canvas::damage(const Extension& ext) { damage(ext.left(), ext.bottom(), ext.right(), ext.top()); } void Canvas::damage(Coord left, Coord bottom, Coord right, Coord top) { CanvasRep& c = *rep(); CanvasDamage& damage = c.damage_; if (c.damaged_) { damage.left = Math::min(damage.left, left); damage.bottom = Math::min(damage.bottom, bottom); damage.right = Math::max(damage.right, right); damage.top = Math::max(damage.top, top); } else { damage.left = left; damage.bottom = bottom; damage.right = right; damage.top = top; } c.new_damage(); } boolean Canvas::damaged(const Extension& ext) const { return damaged(ext.left(), ext.bottom(), ext.right(), ext.top()); } boolean Canvas::damaged( Coord left, Coord bottom, Coord right, Coord top ) const { CanvasRep& c = *rep(); CanvasDamage& damage = c.damage_; return ( c.damaged_ && left < damage.right && right > damage.left && bottom < damage.top && top > damage.bottom ); } void Canvas::damage_area(Extension& ext) { CanvasRep& c = *rep(); CanvasDamage& damage = c.damage_; ext.set_xy(nil, damage.left, damage.bottom, damage.right, damage.top); } void Canvas::damage_all() { CanvasRep& c = *rep(); CanvasDamage& damage = c.damage_; damage.left = 0; damage.bottom = 0; damage.right = c.width_; damage.top = c.height_; c.new_damage(); } boolean Canvas::any_damage() const { return rep()->damaged_; } void Canvas::restrict_damage(const Extension& ext) { restrict_damage(ext.left(), ext.bottom(), ext.right(), ext.top()); } void Canvas::restrict_damage( Coord left, Coord bottom, Coord right, Coord top ) { CanvasRep& c = *rep(); c.clear_damage(); damage(left, bottom, right, top); if (c.repairing_) { c.start_repair(); } } /* * Force a portion of the canvas to be redrawn. This is typically caused * by an X expose event. If there is no damage and we have a buffer * with a copy of the canvas, then we can just copy it to the draw buffer. * Otherwise, we just damage the area. */ void Canvas::redraw(Coord left, Coord bottom, Coord right, Coord top) { CanvasRep& c = *rep(); if (!c.damaged_ && c.copybuffer_ != CanvasRep::unbound) { Display& d = *c.display_; int ptop = d.to_pixels(top); int x = d.to_pixels(left); int y = c.pheight_ - ptop; unsigned int w = d.to_pixels(right) - x; unsigned int h = ptop - d.to_pixels(bottom); XCopyArea( c.dpy(), c.drawbuffer_, c.copybuffer_, c.copygc_, x, y, w, h, x, y ); } else { damage(left, bottom, right, top); } } void Canvas::repair() { CanvasRep& c = *rep(); c.clear_damage(); } /* class CanvasRep */ /* * We must (re)bind the drawbuffer to the canvas' drawable whenever * it changes size or is set to a new xid. This allows us * to (re)allocate a backbuffer pixmap when double-buffering. * We set the canvas' xdrawable to the backbuffer. */ void CanvasRep::bind(boolean double_buffered) { CanvasRep& c = *this; XDisplay* dpy = c.display_->rep()->display_; XGCValues gcv; gcv.graphics_exposures = False; if (double_buffered) { c.drawbuffer_ = XCreatePixmap( dpy, c.xdrawable_, c.pwidth_, c.pheight_, c.window_->rep()->visual_->depth() ); c.copybuffer_ = c.xdrawable_; c.copygc_ = XCreateGC(dpy, c.copybuffer_, GCGraphicsExposures, &gcv); c.xdrawable_ = c.drawbuffer_; } else { c.drawbuffer_ = c.xdrawable_; c.copybuffer_ = CanvasRep::unbound; } c.drawgc_ = XCreateGC(dpy, c.drawbuffer_, GCGraphicsExposures, &gcv); } /* * If double-buffering, free the backbuffer and set the canvas' xdrawable * back to the frontbuffer. */ void CanvasRep::unbind() { CanvasRep& c = *this; if (c.display_ != nil) { XDisplay* dpy = c.dpy(); if (c.copybuffer_ != CanvasRep::unbound) { XFreePixmap(dpy, c.drawbuffer_); c.xdrawable_ = c.copybuffer_; c.copybuffer_ = CanvasRep::unbound; if (c.copygc_ != nil) { XFreeGC(dpy, c.copygc_); c.copygc_ = nil; } } if (c.drawgc_ != nil) { XFreeGC(dpy, c.drawgc_); c.drawgc_ = nil; } } c.drawbuffer_ = CanvasRep::unbound; Resource::unref(c.brush_); Resource::unref(c.color_); Resource::unref(c.font_); c.brush_ = nil; c.color_ = nil; c.font_ = nil; } static inline void restrict(int& c, int a, int b) { if (c < a) { c = a; } else if (c > b) { c = b; } } boolean CanvasRep::start_repair() { CanvasRep& c = *this; if (!c.damaged_) { return false; } XRectangle& clip = c.clip_; CanvasDamage& damage = c.damage_; Display& d = *c.display_; int d_left = d.to_pixels(damage.left); int d_bottom = d.to_pixels(damage.bottom); int d_right = d.to_pixels(damage.right); int d_top = d.to_pixels(damage.top); restrict(d_left, 0, pwidth_); restrict(d_bottom, 0, pheight_); restrict(d_right, 0, pwidth_); restrict(d_top, 0, pheight_); clip.x = d_left; clip.y = c.pheight_ - d_top; clip.width = d_right - d_left; clip.height = d_top - d_bottom; XUnionRectWithRegion(&clip, c.empty_, c.clipping_); XSetClipRectangles(dpy(), c.drawgc_, 0, 0, &clip, 1, YXBanded); c.repairing_ = true; return true; } void CanvasRep::finish_repair() { CanvasRep& c = *this; c.flush(); c.swapbuffers(); c.damaged_ = false; c.on_damage_list_ = false; c.repairing_ = false; } void CanvasRep::flush() { TextRenderInfo* t = &CanvasRep::text_; if (t == nil) { return; } unsigned int nchars = t->cur_text_ - t->text_; if (nchars != 0) { CanvasRep& c = *this; XDisplay* dpy = c.dpy(); XDrawable d = t->canvas_->drawbuffer_; GC gc = t->drawgc_; if (t->spaces_ == 0 || c.text_twobyte_) { if (c.text_twobyte_) { XDrawString16( dpy, d, gc, t->x0_, t->y0_, (XChar2b*)t->text_, nchars/2 ); } else { XDrawString(dpy, d, gc, t->x0_, t->y0_, t->text_, nchars); } } else { int width = XTextWidth(c.xfont_, t->text_, nchars); int delta = c.display_->to_pixels(t->width_) - width; int item = 0; int count = 0; t->items_[item].chars = t->text_; t->items_[item].delta = 0; t->items_[item].font = None; char* text_end = t->cur_text_; for (char* cp = t->text_; cp < text_end; cp++) { if (*cp == ' ') { int d = delta / (t->spaces_ - item); delta -= d; t->items_[item].nchars = count; ++item; t->items_[item].chars = cp; t->items_[item].delta = d; t->items_[item].font = None; count = 0; } ++count; } t->items_[item].nchars = count; XDrawText( dpy, d, gc, t->x0_, t->y0_, t->items_, item + 1 ); } t->cur_text_ = t->text_; t->spaces_ = 0; } } void CanvasRep::swapbuffers() { CanvasRep& c = *this; if (c.copybuffer_ == CanvasRep::unbound) { /* not double-buffering */ return; } XRectangle& clip = c.clip_; XCopyArea( c.dpy(), c.drawbuffer_, c.copybuffer_, c.copygc_, clip.x, clip.y, clip.width, clip.height, clip.x, clip.y ); } XDisplay* CanvasRep::dpy() const { return display_->rep()->display_; } Transformer& CanvasRep::matrix() const { TransformerStack& s = *transformers_; return *(s.item(s.count() - 1)); } void CanvasRep::color(const Color* color) { CanvasRep& c = *this; if (color != nil && color != c.color_) { Resource::ref(color); Resource::unref(c.color_); c.color_ = color; XDisplay* dpy = c.dpy(); WindowVisual* wv = c.window_->rep()->visual_; GC gc = c.drawgc_; ColorRep& cr = *color->rep(wv); c.pixel_ = cr.xcolor_.pixel; c.op_ = cr.op_; c.stipple_ = cr.stipple_; if (cr.masking_) { XSetForeground(dpy, gc, 1); } else if (c.op_ == GXxor) { XSetForeground(dpy, gc, wv->x_or(c.window_->style())); } else { XSetForeground(dpy, gc, c.pixel_); } XSetFunction(dpy, gc, c.op_); if (c.stipple_ != 0) { XSetStipple(dpy, gc, c.stipple_); XSetFillStyle(dpy, gc, FillStippled); } else { XSetFillStyle(dpy, gc, FillSolid); } } } void CanvasRep::brush(const Brush* b) { CanvasRep* c = this; if (b != nil && b != c->brush_) { Resource::ref(b); Resource::unref(c->brush_); c->brush_ = b; XDisplay* dpy = c->dpy(); GC gc = c->drawgc_; BrushRep* br = b->rep(c->display_); c->brush_width_ = br->width_; c->dash_list_ = br->dash_list_; c->dash_count_ = br->dash_count_; if (c->dash_list_ == nil) { XSetLineAttributes( dpy, gc, c->brush_width_, LineSolid, CapButt, JoinMiter ); } else { XSetLineAttributes( dpy, gc, c->brush_width_, LineOnOffDash, CapButt, JoinMiter ); XSetDashes(dpy, gc, 0, c->dash_list_, c->dash_count_); } } } void CanvasRep::font(const Font* f) { CanvasRep& c = *this; if (f != nil && f != c.font_) { Resource::ref(f); Resource::unref(c.font_); FontRep* fr = f->rep(c.display_); c.font_ = f; XFontStruct* xf = fr->font_; c.xfont_ = xf; c.text_twobyte_ = xf->min_byte1 > 0 || xf->max_byte1 > 0; const char* coding = f->Font::encoding(); c.text_reencode_ = coding != nil && strcmp(coding, "ISO8859") == 0; if (fr->unscaled_) { c.font_is_scaled_ = false; } else { c.font_is_scaled_ = true; float tolerance = 0.15; Window* w = c.window_; if (w != nil) { Style* s = w->style(); if (s != nil) { s->find_attribute("fontScaleTolerance", tolerance); } } c.font_is_scaled_ = ( fr->scale_ < (1 - tolerance) || fr->scale_ > (1 + tolerance) ); } XSetFont(c.dpy(), c.drawgc_, xf->fid); } } void CanvasRep::new_damage() { Window* w = window_; damaged_ = true; if (!on_damage_list_ && w != nil && w->bound()) { on_damage_list_ = true; display_->rep()->needs_repair(w); } } void CanvasRep::clear_damage() { damaged_ = false; on_damage_list_ = false; } /* * These are at the end of the file to workaround a cfront 2.0 problem. */ TextRenderInfo CanvasRep::text_; PathRenderInfo CanvasRep::path_; /* Canvas anachronisms */ unsigned int Canvas::Width() const { return pwidth(); } unsigned int Canvas::Height() const { return pheight(); } CanvasLocation Canvas::status() const { return rep()->status_; } #include void Canvas::SetBackground(const Color* c) { Window* ww = window(); if (ww != nil) { WindowRep& w = *ww->rep(); XSetWindowBackground( w.display_->rep()->display_, w.xwindow_, c->rep(w.visual_)->xcolor_.pixel ); } }