/* * 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. */ /* * X11-dependent painter code */ #include "../IV-X11/wtable.h" #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 #include #include #include #include PainterRep::PainterRep() { display = Session::instance()->default_display(); DisplayRep* d = display->rep(); fillgc = XCreateGC(d->display_, d->root_, 0, nil); dashgc = XCreateGC(d->display_, d->root_, 0, nil); fillbg = true; overwrite = false; x_or = false; clipped = false; } PainterRep::~PainterRep() { XDisplay* dpy = display->rep()->display_; XFreeGC(dpy, fillgc); XFreeGC(dpy, dashgc); } void PainterRep::PrepareFill(const Pattern* p) { PatternRep& pr = *p->rep(); XDisplay* dpy = display->rep()->display_; if (pr.pixmap_ == nil) { XSetFillStyle(dpy, fillgc, FillSolid); } else if (fillbg) { XSetStipple(dpy, fillgc, pr.pixmap_); XSetFillStyle(dpy, fillgc, FillOpaqueStippled); } else { XSetStipple(dpy, fillgc, pr.pixmap_); XSetFillStyle(dpy, fillgc, FillStippled); } } void PainterRep::PrepareDash(const Brush* b) { BrushRep& br = *(b->rep(display)); XDisplay* dpy = display->rep()->display_; if (br.dash_list_ == nil) { XSetLineAttributes( dpy, dashgc, br.width_, LineSolid, CapButt, JoinMiter ); } else { XSetLineAttributes( dpy, dashgc, br.width_, LineOnOffDash, CapButt, JoinMiter ); XSetDashes(dpy, dashgc, 0, br.dash_list_, br.dash_count_); } } /* * Short-hand for allocating a vector of X points. * The idea is to use a static array if possible; otherwise * allocate/deallocate off the heap. */ static const int XPointListSize = 200; static XPoint xpoints[XPointListSize]; static XPoint* AllocPts(int n) { return (n <= XPointListSize) ? xpoints : new XPoint[n]; } static void FreePts(XPoint* v) { if (v != xpoints) { delete v; } } Painter::Painter() { rep = new PainterRep; Init(); } Painter::Painter(Painter* copy) { rep = new PainterRep; XDisplay* dpy = rep->display->rep()->display_; rep->fillbg = copy->rep->fillbg; rep->overwrite = copy->rep->overwrite; Copy(copy); if (copy->rep->x_or) { Begin_xor(); } rep->x_or = copy->rep->x_or; if (rep->overwrite) { XSetSubwindowMode(dpy, rep->fillgc, IncludeInferiors); XSetSubwindowMode(dpy, rep->dashgc, IncludeInferiors); } } Painter::~Painter() { Resource::unref(matrix); Resource::unref(font); Resource::unref(br); Resource::unref(foreground); Resource::unref(background); Resource::unref(pattern); delete rep; } void Painter::FillBg(boolean b) { if (rep->fillbg != b) { if (rep->x_or) { End_xor(); } rep->fillbg = b; if (pattern != nil) { rep->PrepareFill(pattern); } if (br != nil) { rep->PrepareDash(br); } } } boolean Painter::BgFilled() const { return rep->fillbg; } void Painter::SetColors(const Color* f, const Color* b) { if (rep->x_or) { End_xor(); } /* * Make sure to reference both new colors before unreferencing the old * in case the call is swapping colors. */ if (foreground != f) { Resource::ref(f); } if (background != b) { Resource::ref(b); } XDisplay* dpy = rep->display->rep()->display_; if (f != nil && foreground != f) { Resource::unref(foreground); foreground = f; unsigned long pixel = foreground->PixelValue(); XSetForeground(dpy, rep->fillgc, pixel); XSetForeground(dpy, rep->dashgc, pixel); } if (b != nil && background != b) { Resource::unref(background); background = b; unsigned long pixel = background->PixelValue(); XSetBackground(dpy, rep->fillgc, pixel); XSetBackground(dpy, rep->dashgc, pixel); } } void Painter::SetPattern(const Pattern* pat) { if (rep->x_or) { End_xor(); } if (pattern != pat) { Resource::ref(pat); Resource::unref(pattern); pattern = pat; if (pattern != nil) { rep->PrepareFill(pattern); } } } void Painter::SetBrush(const Brush* b) { if (rep->x_or) { End_xor(); } if (br != b) { Resource::ref(b); Resource::unref(br); br = b; if (br != nil) { rep->PrepareDash(br); } } } void Painter::SetFont(const Font* f) { if (font != f) { Resource::ref(f); Resource::unref(font); font = f; if (font != nil) { Display* d = rep->display; XSetFont( d->rep()->display_, rep->fillgc, font->rep(d)->font_->fid ); } } } void Painter::Clip( Canvas* c, IntCoord left, IntCoord bottom, IntCoord right, IntCoord top ) { XRectangle& r = rep->xclip[0]; IntCoord x, y; XDisplay* d = rep->display->rep()->display_; if (left > right) { x = right; r.width = left - right + 1; } else { x = left; r.width = right - left + 1; } if (bottom > top) { y = bottom; r.height = bottom - top + 1; } else { y = top; r.height = top - bottom + 1; } r.x = x; r.y = c->pheight() - 1 - y; if (r.x == 0 && r.y == 0 && r.width == c->pwidth() && r.height == c->pheight() ) { /* clipping to entire canvas is equivalent to no clipping at all */ NoClip(); } else { rep->clipped = true; XSetClipRectangles(d, rep->fillgc, 0, 0, rep->xclip, 1, Unsorted); XSetClipRectangles(d, rep->dashgc, 0, 0, rep->xclip, 1, Unsorted); } } void Painter::NoClip() { XDisplay* dpy = rep->display->rep()->display_; rep->clipped = false; XSetClipMask(dpy, rep->fillgc, None); XSetClipMask(dpy, rep->dashgc, None); } void Painter::SetOverwrite(boolean children) { if (rep->overwrite != children) { XDisplay* dpy = rep->display->rep()->display_; rep->overwrite = children; XSetSubwindowMode( dpy, rep->fillgc, children ? IncludeInferiors : ClipByChildren ); XSetSubwindowMode( dpy, rep->dashgc, children ? IncludeInferiors : ClipByChildren ); } } void Painter::SetPlaneMask(int m) { XDisplay* dpy = rep->display->rep()->display_; XSetPlaneMask(dpy, rep->fillgc, m); XSetPlaneMask(dpy, rep->dashgc, m); } void Painter::Map( Canvas* c, IntCoord x, IntCoord y, IntCoord& mx, IntCoord& my ) { if (matrix == nil) { mx = x; my = y; } else { matrix->Transform(x, y, mx, my); } mx += xoff; my = c->pheight() - 1 - (my + yoff); } void Painter::MapList( Canvas* c, IntCoord x[], IntCoord y[], int n, IntCoord mx[], IntCoord my[] ) { IntCoord* xp, * yp, * mxp, * myp; IntCoord* lim; xp = x; yp = y; mxp = mx; myp = my; lim = &x[n]; if (matrix == nil) { for (; xp < lim; xp++, yp++, mxp++, myp++) { *mxp = *xp + xoff; *myp = c->pheight() - 1 - (*yp + yoff); } } else { for (; xp < lim; xp++, yp++, mxp++, myp++) { matrix->Transform(*xp, *yp, *mxp, *myp); *mxp += xoff; *myp = c->pheight() - 1 - (*myp + yoff); } } } void Painter::MapList( Canvas* c, float x[], float y[], int n, IntCoord mx[], IntCoord my[] ) { register float* xp, * yp; register IntCoord* mxp, * myp; float tmpx, tmpy, * lim; xp = x; yp = y; mxp = mx; myp = my; lim = &x[n]; if (matrix == nil) { for (; xp < lim; xp++, yp++, mxp++, myp++) { *mxp = Math::round(*xp + xoff); *myp = Math::round(c->pheight() - 1 - (*yp + yoff)); } } else { for (; xp < lim; xp++, yp++, mxp++, myp++) { matrix->Transform(*xp, *yp, tmpx, tmpy); *mxp = Math::round(tmpx + xoff); *myp = Math::round(c->pheight() - 1 - (tmpy + yoff)); } } } void Painter::Begin_xor() { if (!rep->x_or) { rep->x_or = true; DisplayRep& d = *rep->display->rep(); XDisplay* dpy = d.display_; unsigned long xor_pixel = d.default_visual_->x_or(*d.style_); XSetFunction(dpy, rep->fillgc, GXxor); XSetForeground(dpy, rep->fillgc, xor_pixel); XSetFillStyle(dpy, rep->fillgc, FillSolid); XSetFunction(dpy, rep->dashgc, GXxor); XSetForeground(dpy, rep->dashgc, xor_pixel); XSetFillStyle(dpy, rep->dashgc, FillSolid); } } void Painter::End_xor() { if (rep->x_or) { rep->x_or = false; XDisplay* dpy = rep->display->rep()->display_; XSetFunction(dpy, rep->fillgc, GXcopy); unsigned long pixel = foreground->PixelValue(); XSetForeground(dpy, rep->fillgc, pixel); if (pattern != nil) { rep->PrepareFill(pattern); } XSetFunction(dpy, rep->dashgc, GXcopy); XSetForeground(dpy, rep->dashgc, pixel); if (br != nil) { rep->PrepareDash(br); } } } inline unsigned 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 TxKey(Transformer* t, int x, int y) { if (t == nil) { return 0; } else { float x1, y1, x2, y2, x3, y3; t->matrix(x1, y1, x2, y2, x3, y3); x1 = x1 - 1.0; y2 = y2 - 1.0; return ( (_txkey((int)(x1*x)) << 24) + (_txkey((int)(y1*y)) << 16) + (_txkey((int)(x2*x)) << 8) + (_txkey((int)(y2*y))) ); } } /* * Per-display shared painter information. */ class Bitmap; declareTable2(BitmapTable,XFont,int,Bitmap*) implementTable2(BitmapTable,XFont,int,Bitmap*) declareTable2(RasterTable,const Raster*,int,RasterRep*) implementTable2(RasterTable,const Raster*,int,RasterRep*) class PainterDpyInfoList; class PainterDpyInfo { public: Display* display_; BitmapTable* btable_; BitmapTable* txtable_; RasterTable* tx_rasters_; static PainterDpyInfoList* info_list_; enum { TxFontsDefault, TxFontsOff, TxFontsOn, TxFontsCache } txfonts; enum { TxImagesDefault, TxImagesAuto, TxImagesDest, TxImagesSource } tximages; static PainterDpyInfo* find(Display*); int tx_key(const Transformer&, Coord x, Coord y); Bitmap* get_char_bitmap(const Font*, int c, int k, const Transformer&); RasterRep* tx_raster(const Raster*, const Transformer&); }; PainterDpyInfoList* PainterDpyInfo::info_list_; declarePtrList(PainterDpyInfoList,PainterDpyInfo); implementPtrList(PainterDpyInfoList,PainterDpyInfo); PainterDpyInfo* PainterDpyInfo::find(Display* d) { if (info_list_ == nil) { info_list_ = new PainterDpyInfoList(3); } for (ListItr(PainterDpyInfoList) i(*info_list_); i.more(); i.next()) { PainterDpyInfo* p = i.cur(); if (p->display_ == d) { return p; } } PainterDpyInfo* p = new PainterDpyInfo; p->display_ = d; p->btable_ = nil; p->txtable_ = nil; p->tx_rasters_ = nil; p->txfonts = PainterDpyInfo::TxFontsDefault; p->tximages = PainterDpyInfo::TxImagesDefault; String v; if (d->style()->find_attribute("TransformFonts", v)) { if (v.case_insensitive_equal("on")) { p->txfonts = PainterDpyInfo::TxFontsOn; } else if (v.case_insensitive_equal("off")) { p->txfonts = PainterDpyInfo::TxFontsOff; } else if (v.case_insensitive_equal("cache")) { p->txfonts = PainterDpyInfo::TxFontsCache; } } if (d->style()->find_attribute("TransformImages", v)) { if (v.case_insensitive_equal("auto")) { p->tximages = PainterDpyInfo::TxImagesAuto; } else if (v.case_insensitive_equal("off")) { p->tximages = PainterDpyInfo::TxImagesDest; } else if (v.case_insensitive_equal("source")) { p->tximages = PainterDpyInfo::TxImagesSource; } } info_list_->append(p); return p; } int PainterDpyInfo::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; } Bitmap* PainterDpyInfo::get_char_bitmap( const Font* f, int c, int k, const Transformer& t ) { if (btable_ == nil) { btable_ = new BitmapTable(256); txtable_ = new BitmapTable(1024); } Bitmap* basic; XFont fid = f->rep(display_)->font_->fid; if (!btable_->find(basic, fid, c)) { basic = new Bitmap(f, c); Resource::ref(basic); btable_->insert(fid, c, basic); } Bitmap* tx; Pixmap mapid = basic->rep()->pixmap_; if (!txtable_->find(tx, mapid, k)) { tx = new Bitmap(*basic); Resource::ref(tx); tx->Transform(&t); txtable_->insert(mapid, k, tx); } return tx; } RasterRep* PainterDpyInfo::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 RasterTable(1024); } RasterRep* rep; if (!tx_rasters_->find(rep, r, key)) { Display* d = r->rep()->display_; 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; } DisplayRep& dr = *d->rep(); 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 Painter::Stencil( Canvas* c, IntCoord x, IntCoord y, Bitmap* bitmap, Bitmap* mask ) { if (c == nil) { return; } Display* d = rep->display; XDisplay* dpy = d->rep()->display_; XDrawable xid = c->rep()->xdrawable_; if (xid == CanvasRep::unbound) { return; } if (rep->x_or) { End_xor(); } int tx = TxKey(matrix, bitmap->pwidth(), bitmap->pheight()); if (tx == 0) { IntCoord dx, dy; IntCoord dl = x - d->to_pixels(bitmap->left_bearing()); IntCoord dt = y + d->to_pixels(bitmap->ascent()); Map(c, dl, dt - 1, dx, dy); if (mask == nil) { XCopyPlane( dpy, bitmap->rep()->pixmap_, xid, rep->fillgc, 0, 0, bitmap->pwidth(), bitmap->pheight(), dx, dy, 1 ); } else if (mask == bitmap) { XSetForeground(dpy, rep->fillgc, 0); XSetBackground(dpy, rep->fillgc, AllPlanes); XSetFunction(dpy, rep->fillgc, GXand); XCopyPlane( dpy, bitmap->rep()->pixmap_, xid, rep->fillgc, 0, 0, bitmap->pwidth(), bitmap->pheight(), dx, dy, 1 ); XSetForeground(dpy, rep->fillgc, foreground->PixelValue()); XSetBackground(dpy, rep->fillgc, 0); XSetFunction(dpy, rep->fillgc, GXxor); XCopyPlane( dpy, bitmap->rep()->pixmap_, xid, rep->fillgc, 0, 0, bitmap->pwidth(), bitmap->pheight(), dx, dy, 1 ); XSetBackground(dpy, rep->fillgc, background->PixelValue()); XSetFunction(dpy, rep->fillgc, GXcopy); } else { IntCoord mx, my; IntCoord ml = x - d->to_pixels(mask->left_bearing()); IntCoord mt = y + d->to_pixels(mask->ascent()); Map(c, ml, mt - 1, mx, my); GC gc = XCreateGC(dpy, d->rep()->root_, 0, nil); XSetForeground(dpy, gc, foreground->PixelValue()); XSetBackground(dpy, gc, background->PixelValue()); XSetGraphicsExposures(dpy, gc, False); XSetClipOrigin(dpy, gc, mx, my); XSetClipMask(dpy, gc, mask->rep()->pixmap_); XCopyPlane( dpy, bitmap->rep()->pixmap_, xid, gc, 0, 0, bitmap->pwidth(), bitmap->pheight(), dx, dy, 1 ); XFreeGC(dpy, gc); } } else { bitmap->rep()->fill(); if (mask != nil) { mask->rep()->fill(); DrawTransformedImage( bitmap->rep()->image_, x - d->to_pixels(bitmap->left_bearing()), y - d->to_pixels(bitmap->descent()), mask->rep()->image_, x - d->to_pixels(mask->left_bearing()), y - d->to_pixels(mask->descent()), xid, c->pheight(), -xoff, -yoff, true, foreground->PixelValue(), background->PixelValue(), rep->fillgc, *matrix ); } else { DrawTransformedImage( bitmap->rep()->image_, x - d->to_pixels(bitmap->left_bearing()), y - d->to_pixels(bitmap->descent()), nil, 0, 0, xid, c->pheight(), -xoff, -yoff, true, foreground->PixelValue(), background->PixelValue(), rep->fillgc, *matrix ); } } } void Painter::RasterRect(Canvas* c, IntCoord x, IntCoord y, Raster* r) { if (c == nil || r == nil) { return; } Display* d = r->rep()->display_; XDisplay* dpy = d->rep()->display_; XDrawable xid = c->rep()->xdrawable_; if (xid == CanvasRep::unbound) { return; } r->flush(); PainterDpyInfo& p = *PainterDpyInfo::find(rep->display); RasterRep* info = (matrix == nil) ? r->rep() : p.tx_raster(r, *matrix); IntCoord rw = (unsigned int)r->pwidth(); IntCoord rh = (unsigned int)r->pheight(); IntCoord x1, y1, x2, y2, x3, y3, x4, y4; Map(c, x, y, x1, y1); Map(c, x, y + rh, x2, y2); Map(c, x + rw, y + rh, x3, y3); Map(c, x + rw, y, x4, y4); IntCoord xmin = Math::min(x1, x2, x3, x4); IntCoord ymin = Math::min(y1, y2, y3, y4); XPoint clip_area[4]; clip_area[0].x = x1; clip_area[0].y = y1; clip_area[1].x = x2; clip_area[1].y = y2; clip_area[2].x = x3; clip_area[2].y = y3; clip_area[3].x = x4; clip_area[3].y = y4; Region rg = XPolygonRegion(clip_area, 4, EvenOddRule); if (rep->clipped) { Region tmp = XCreateRegion(); XUnionRectWithRegion(&rep->xclip[0], tmp, tmp); XIntersectRegion(rg, tmp, rg); XDestroyRegion(tmp); } XSetRegion(dpy, rep->fillgc, rg); XSetGraphicsExposures(dpy, rep->fillgc, False); XCopyArea( dpy, info->pixmap_, xid, rep->fillgc, 0, 0, info->pwidth_, info->pheight_, xmin, ymin ); XSetGraphicsExposures(dpy, rep->fillgc, True); XDestroyRegion(rg); if (rep->clipped) { XSetClipRectangles(dpy, rep->fillgc, 0, 0, rep->xclip, 1, Unsorted); } else { NoClip(); } } void Painter::Text(Canvas* c, const char* s, int len, IntCoord x, IntCoord y) { if (c == nil || s == nil || len == 0) { return; } XDisplay* d = rep->display->rep()->display_; XDrawable xid = c->rep()->xdrawable_; if (xid == CanvasRep::unbound) { return; } IntCoord x0, y0; IntCoord ybase = y + font->Baseline() + 1; IntCoord ytop = y + font->Height(); int txstring = TxKey(matrix, font->Width(s, len), font->Height()); if (style & Reversed) { SetColors(GetBgColor(), GetFgColor()); } if (txstring == 0) { Map(c, x, ybase - 1, x0, y0); if (rep->fillbg) { XDrawImageString( d, xid, rep->fillgc, x0, y0, s, len ); } else { XDrawString( d, xid, rep->fillgc, x0, y0, s, len ); } if (style & Boldface) { XDrawString( d, xid, rep->fillgc, x0-1, y0, s, len ); } } else { IntCoord curx = x; float fx0, fy0; Transformer notrans(*matrix); notrans.Transform(0.0, 0.0, fx0, fy0); notrans.Translate(-fx0, -fy0); int txchar = TxKey(matrix, font->Width("M"), font->Height()); Bitmap* bits; PainterDpyInfo& p = *PainterDpyInfo::find(rep->display); Transformer* oldmatrix; for (int i = 0; i < len; ++i) { IntCoord nextx = curx + font->Width(s+i, 1); if (rep->fillbg) { ClearRect(c, curx, y, nextx, ytop); } switch (p.txfonts) { case PainterDpyInfo::TxFontsOff: Map(c, curx, ybase - 1, x0, y0); XDrawString( d, xid, rep->fillgc, x0, y0, s+i, 1 ); if (style & Boldface) { XDrawString( d, xid, rep->fillgc, x0-1, y0, s+i, 1 ); } break; case PainterDpyInfo::TxFontsOn: bits = new Bitmap(font, s[i]); Stencil(c, curx, ybase, bits, bits); if (style & Boldface) { Stencil(c, curx-1, ybase, bits, bits); } break; case PainterDpyInfo::TxFontsCache: case PainterDpyInfo::TxFontsDefault: bits = p.get_char_bitmap(font, s[i], txchar, notrans); oldmatrix = matrix; matrix = nil; oldmatrix->Transform(curx, ybase, x0, y0); Stencil(c, x0, y0, bits, bits); if (style & Boldface) { oldmatrix->Transform(curx+1, ybase, x0, y0); Stencil(c, x0, y0, bits, bits); } matrix = oldmatrix; break; } curx = nextx; } } if (style & Outlined) { /* unimplemented */ } if (style & Underlined) { Line(c, x, ybase, x + font->Width(s, len) - 1, ybase); } if (style & Reversed) { SetColors(GetBgColor(), GetFgColor()); } } void Painter::Point(Canvas* c, IntCoord x, IntCoord y) { if (c == nil) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } IntCoord mx, my; Map(c, x, y, mx, my); XDrawPoint(cr->dpy(), cr->xdrawable_, rep->fillgc, mx, my); } void Painter::MultiPoint(Canvas* c, IntCoord x[], IntCoord y[], int n) { if (c == nil || n == 0) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } register XPoint* v = AllocPts(n); for (register int i = 0; i < n; i++) { Map(c, x[i], y[i], v[i].x, v[i].y); } XDrawPoints(cr->dpy(), cr->xdrawable_, rep->fillgc, v, n, CoordModeOrigin); FreePts(v); } void Painter::Line( Canvas* c, IntCoord x1, IntCoord y1, IntCoord x2, IntCoord y2 ) { if (c == nil) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } IntCoord mx1, my1, mx2, my2; Map(c, x1, y1, mx1, my1); Map(c, x2, y2, mx2, my2); XDrawLine(cr->dpy(), cr->xdrawable_, rep->dashgc, mx1, my1, mx2, my2); } void Painter::Rect( Canvas* c, IntCoord x1, IntCoord y1, IntCoord x2, IntCoord y2 ) { if (c == nil) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } if (matrix != nil && matrix->Rotated() && !matrix->Rotated90()) { IntCoord x[4], y[4]; x[0] = x[3] = x1; x[1] = x[2] = x2; y[0] = y[1] = y1; y[2] = y[3] = y2; Polygon(c, x, y, 4); } else { IntCoord left, bottom, right, top, tmp; int w, h; Map(c, x1, y1, left, bottom); Map(c, x2, y2, right, top); if (left > right) { tmp = left; left = right; right = tmp; } if (top > bottom) { tmp = bottom; bottom = top; top = tmp; } w = right - left; h = bottom - top; XDrawRectangle( cr->dpy(), cr->xdrawable_, rep->dashgc, left, top, w, h ); } } void Painter::FillRect( Canvas* c, IntCoord x1, IntCoord y1, IntCoord x2, IntCoord y2 ) { if (c == nil) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } if (matrix != nil && matrix->Rotated() && !matrix->Rotated90()) { IntCoord x[4], y[4]; x[0] = x[3] = x1; x[1] = x[2] = x2; y[0] = y[1] = y1; y[2] = y[3] = y2; FillPolygon(c, x, y, 4); } else { IntCoord left, bottom, right, top, tmp; Map(c, x1, y1, left, bottom); Map(c, x2, y2, right, top); if (left > right) { tmp = left; left = right; right = tmp; } if (top > bottom) { tmp = bottom; bottom = top; top = tmp; } unsigned int w = right - left + 1; unsigned int h = bottom - top + 1; XFillRectangle( cr->dpy(), cr->xdrawable_, rep->fillgc, left, top, w, h ); } } void Painter::ClearRect( Canvas* c, IntCoord x1, IntCoord y1, IntCoord x2, IntCoord y2 ) { if (c == nil) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } XDisplay* dpy = cr->dpy(); XSetForeground(dpy, rep->fillgc, background->PixelValue()); XSetFillStyle(dpy, rep->fillgc, FillSolid); FillRect(c, x1, y1, x2, y2); XSetForeground(dpy, rep->fillgc, foreground->PixelValue()); rep->PrepareFill(pattern); } void Painter::Circle(Canvas* c, IntCoord x, IntCoord y, int r) { if (c == nil || r == 0) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } if (matrix != nil && (matrix->Stretched() || matrix->Rotated())) { Ellipse(c, x, y, r, r); } else { IntCoord left, top, right, bottom, tmp; Map(c, x-r, y+r, left, top); Map(c, x+r, y-r, right, bottom); if (left > right) { tmp = left; left = right; right = tmp; } if (top > bottom) { tmp = bottom; bottom = top; top = tmp; } XDrawArc( cr->dpy(), cr->xdrawable_, rep->dashgc, left, top, right-left, bottom-top, 0, 360*64 ); } } void Painter::FillCircle(Canvas* c, IntCoord x, IntCoord y, int r) { if (c == nil || r == 0) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } if (matrix != nil && (matrix->Stretched() || matrix->Rotated())) { FillEllipse(c, x, y, r, r); } else { IntCoord left, top, right, bottom, tmp; Map(c, x-r, y+r, left, top); Map(c, x+r, y-r, right, bottom); if (left > right) { tmp = left; left = right; right = tmp; } if (top > bottom) { tmp = bottom; bottom = top; top = tmp; } XFillArc( cr->dpy(), cr->xdrawable_, rep->fillgc, left, top, right-left, bottom-top, 0, 360*64 ); } } void Painter::MultiLine(Canvas* c, IntCoord x[], IntCoord y[], int n) { if (c == nil || n == 0) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } register XPoint* v = AllocPts(n); for (register int i = 0; i < n; i++) { Map(c, x[i], y[i], v[i].x, v[i].y); } XDrawLines(cr->dpy(), cr->xdrawable_, rep->dashgc, v, n, CoordModeOrigin); FreePts(v); } void Painter::MultiLineNoMap(Canvas* c, IntCoord x[], IntCoord y[], int n) { if (c == nil || n == 0) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } register XPoint* v = AllocPts(n); for (register int i = 0; i < n; i++) { v[i].x = x[i]; v[i].y = y[i]; } XDrawLines(cr->dpy(), cr->xdrawable_, rep->dashgc, v, n, CoordModeOrigin); FreePts(v); } void Painter::Polygon(Canvas* c, IntCoord x[], IntCoord y[], int n) { if (c == nil || n==0) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } register XPoint* v = AllocPts(n+1); register int i; for (i = 0; i < n; i++) { Map(c, x[i], y[i], v[i].x, v[i].y); } if (x[i-1] != x[0] || y[i-1] != y[0]) { v[i] = v[0]; ++i; } XDrawLines(cr->dpy(), cr->xdrawable_, rep->dashgc, v, i, CoordModeOrigin); FreePts(v); } void Painter::FillPolygonNoMap(Canvas* c, IntCoord x[], IntCoord y[], int n) { if (c == nil || n == 0) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } register XPoint* v = AllocPts(n); for (register int i = 0; i < n; i++) { v[i].x = x[i]; v[i].y = y[i]; } XFillPolygon( cr->dpy(), cr->xdrawable_, rep->fillgc, v, n, Complex, CoordModeOrigin ); FreePts(v); } void Painter::FillPolygon(Canvas* c, IntCoord x[], IntCoord y[], int n) { if (c == nil || n == 0) { return; } CanvasRep* cr = c->rep(); if (cr->xdrawable_ == CanvasRep::unbound) { return; } register XPoint* v = AllocPts(n+1); for (register int i = 0; i < n; i++) { Map(c, x[i], y[i], v[i].x, v[i].y); } XFillPolygon( cr->dpy(), cr->xdrawable_, rep->fillgc, v, n, Complex, CoordModeOrigin ); FreePts(v); } void Painter::Copy( Canvas* src, IntCoord x1, IntCoord y1, IntCoord x2, IntCoord y2, Canvas* dst, IntCoord x0, IntCoord y0 ) { if (src == nil || dst == nil) { return; } XDisplay* dpy = dst->rep()->dpy(); XDrawable src_xid = src->rep()->xdrawable_; XDrawable dst_xid = dst->rep()->xdrawable_; if (src_xid == CanvasRep::unbound || dst_xid == CanvasRep::unbound) { return; } IntCoord sx1, sy1, sx2, sy2, sx3, sy3, sx4, sy4, dx1, dy1; if (matrix == nil) { sx1 = x1; sy1 = y1; sx2 = x1; sy2 = y2; sx3 = x2; sy3 = y2; sx4 = x2; sy4 = y1; dx1 = x0; dy1 = y0; } else { Transformer t(*matrix); t.Transform(x1, y1, sx1, sy1); t.Transform(x1, y2, sx2, sy2); t.Transform(x2, y2, sx3, sy3); t.Transform(x2, y1, sx4, sy4); t.Transform(x0, y0, dx1, dy1); } int minx = Math::min(sx1, sx2, sx3, sx4); int maxx = Math::max(sx1, sx2, sx3, sx4); int miny = Math::min(sy1, sy2, sy3, sy4); int maxy = Math::max(sy1, sy2, sy3, sy4); int w = maxx - minx + 1; int h = maxy - miny + 1; int sx = minx + xoff; int sy = src->pheight() - 1 - (maxy + yoff); int dx = dx1 - (sx1 - minx) + xoff; int dy = dst->pheight() - 1 - (dy1 - (sy1 - maxy) + yoff); if ((sx1 == sx2 || sy1 == sy2) && (sx1 == sx4 || sy1 == sy4)) { if (src->status() == Canvas::offscreen) { XSetGraphicsExposures(dpy, rep->fillgc, False); XCopyArea( dpy, src_xid, dst_xid, rep->fillgc, sx, sy, w, h, dx, dy ); XSetGraphicsExposures(dpy, rep->fillgc, True); } else { XCopyArea( dpy, src_xid, dst_xid, rep->fillgc, sx, sy, w, h, dx, dy ); dst->rep()->wait_for_copy(); } } else { GC copygc = XCreateGC(dpy, dst_xid, 0, nil); Pixmap mask; mask = XCreatePixmap(dpy, rep->display->rep()->root_, w, h, 1); GC maskgc = XCreateGC(dpy, mask, 0, nil); XSetForeground(dpy, maskgc, 0); XFillRectangle(dpy, mask, maskgc, 0, 0, w, h); XSetForeground(dpy, maskgc, 1); XPoint v[4]; v[0].x = sx1 - minx; v[0].y = maxy - sy1; v[1].x = sx2 - minx; v[1].y = maxy - sy2; v[2].x = sx3 - minx; v[2].y = maxy - sy3; v[3].x = sx4 - minx; v[3].y = maxy - sy4; XFillPolygon(dpy, mask, maskgc, v, 4, Convex, CoordModeOrigin); XFreeGC(dpy, maskgc); XSetClipOrigin(dpy, copygc, dx, dy); XSetClipMask(dpy, copygc, mask); if (src->status() == Canvas::offscreen) { XSetGraphicsExposures(dpy, copygc, False); XCopyArea( dpy, src_xid, dst_xid, copygc, sx, sy, w, h, dx, dy ); XSetGraphicsExposures(dpy, copygc, True); } else { XCopyArea( dpy, src_xid, dst_xid, copygc, sx, sy, w, h, dx, dy ); dst->rep()->wait_for_copy(); } XFreePixmap(dpy, mask); XFreeGC(dpy, copygc); } } /* anachronism */ void CanvasRep::wait_for_copy() { Event e; XEvent& xe = e.rep()->xevent_; DisplayRep* r = display_->rep(); XDisplay* dpy = r->display_; WindowTable* wt = r->wtable_; Window* w; boolean keep_waiting = true; while (keep_waiting) { XWindowEvent(dpy, xdrawable_, ExposureMask, &xe); switch (xe.type) { case NoExpose: keep_waiting = false; break; case Expose: if (wt->find(w, xe.xexpose.window)) { w->receive(e); } break; case GraphicsExpose: if (wt->find(w, xe.xgraphicsexpose.drawable)) { w->receive(e); } if (xe.xgraphicsexpose.count == 0) { keep_waiting = false; } break; } } }