Back to index

texmacs  1.0.7.15
x_drawable.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : x_drawables.cpp
00004 * DESCRIPTION: Drawables under X11
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "X11/x_window.hpp"
00013 #include "file.hpp"
00014 #include "image_files.hpp"
00015 #include "sys_utils.hpp"
00016 #include "analyze.hpp"
00017 #include "iterator.hpp"
00018 #include "Ghostscript/ghostscript.hpp"
00019 #include "Imlib2/imlib2.hpp"
00020 
00021 extern int  nr_windows;
00022 extern bool reverse_colors;
00023 extern hashmap<tree,string> ps_bbox;
00024 
00025 /******************************************************************************
00026 * Constructors and destructors
00027 ******************************************************************************/
00028 
00029 x_drawable_rep::x_drawable_rep (x_gui gui2, x_window_rep* x_win2):
00030   gui (gui2), x_win (x_win2), w (0), h (0)
00031 {
00032   dpy         = gui->dpy;
00033   gc          = gui->gc;
00034   cur_fg      = black;
00035   cur_bg      = white;
00036 }
00037 
00038 x_drawable_rep::x_drawable_rep (x_gui gui2, int w2, int h2):
00039   gui (gui2), x_win (NULL), w (w2), h (h2)
00040 {
00041   dpy         = gui->dpy;
00042   gc          = gui->gc;
00043   cur_fg      = black;
00044   cur_bg      = white;
00045   win= (Drawable) XCreatePixmap (gui->dpy, gui->root, w, h, gui->depth);
00046 }
00047 
00048 x_drawable_rep::~x_drawable_rep () {
00049   if (x_win == NULL)
00050     XFreePixmap (gui->dpy, (Pixmap) win);
00051 }
00052 
00053 bool
00054 x_drawable_rep::is_x_drawable () {
00055   return true;
00056 }
00057 
00058 x_drawable_rep*
00059 x_drawable_rep::as_x_drawable () {
00060   return this;
00061 }
00062 
00063 void
00064 x_drawable_rep::get_extents (int& w2, int& h2) {
00065   if (x_win != NULL) return x_win->get_extents (w2, h2);
00066   else {
00067     w2= w;
00068     h2= h;
00069   }
00070 }
00071 
00072 bool
00073 x_drawable_rep::interrupted (bool check) {
00074   return gui->check_event (check? INTERRUPT_EVENT: INTERRUPTED_EVENT);
00075 }
00076 
00077 /******************************************************************************
00078 * Conversion between window and postscript coordinates
00079 ******************************************************************************/
00080 
00081 void
00082 x_drawable_rep::encode (SI& x, SI& y) {
00083   x= (x*pixel) - ox;
00084   y= ((-y)*pixel) - oy;
00085 }
00086 
00087 void
00088 x_drawable_rep::decode (SI& x, SI& y) {
00089   x += ox; y += oy;
00090   if (x>=0) x= x/pixel; else x= (x-pixel+1)/pixel;
00091   if (y>=0) y= -(y/pixel); else y= -((y-pixel+1)/pixel);
00092 }
00093 
00094 /******************************************************************************
00095 * Clipping
00096 ******************************************************************************/
00097 
00098 void
00099 x_drawable_rep::set_clipping (SI x1, SI y1, SI x2, SI y2, bool restore) {
00100   (void) restore;
00101   outer_round (x1, y1, x2, y2);
00102   renderer_rep::set_clipping (x1, y1, x2, y2);
00103   Region region= XCreateRegion ();
00104   decode (x1, y1);
00105   decode (x2, y2);
00106   XRectangle r;
00107   r.x     = x1;
00108   r.y     = y2;
00109   r.width = x2-x1;
00110   r.height= y1-y2;
00111   XUnionRectWithRegion (&r, region, region);
00112   XSetRegion (dpy, gc, region);
00113   XDestroyRegion (region);
00114 }
00115 
00116 /******************************************************************************
00117 * Drawing into drawables
00118 ******************************************************************************/
00119 
00120 color
00121 x_drawable_rep::get_color () {
00122   return cur_fg;
00123 }
00124 
00125 color
00126 x_drawable_rep::get_background () {
00127   return cur_bg;
00128 }
00129 
00130 void
00131 x_drawable_rep::set_color (color c) {
00132   cur_fg= c;
00133   XSetForeground (dpy, gc, CONVERT (blend (cur_fg, cur_bg)));
00134 }
00135 
00136 void
00137 x_drawable_rep::set_background (color c) {
00138   cur_bg= c;
00139   XSetBackground (dpy, gc, CONVERT (cur_bg));
00140 }
00141 
00142 void
00143 x_drawable_rep::set_line_style (SI lw, int type, bool round) { (void) type;
00144   if (lw <= pixel)
00145     XSetLineAttributes (dpy, (GC) gc, 1,
00146                      LineSolid, round?CapRound:CapButt, JoinRound);
00147   else
00148     XSetLineAttributes (dpy, (GC) gc, (lw+thicken) / pixel,
00149                      LineSolid, round?CapRound:CapButt, JoinRound);
00150 }
00151 
00152 void
00153 x_drawable_rep::line (SI x1, SI y1, SI x2, SI y2) {
00154   decode (x1, y1);
00155   decode (x2, y2);
00156   y1--; y2--; // top-left origin to bottom-left origin conversion
00157   XDrawLine (dpy, win, gc, x1, y1, x2, y2);
00158 }
00159 
00160 void
00161 x_drawable_rep::lines (array<SI> x, array<SI> y) {
00162   int i, n= N(x);
00163   if ((N(y) != n) || (n<1)) return;
00164   STACK_NEW_ARRAY (pnt, XPoint, n);
00165   for (i=0; i<n; i++) {
00166     SI xx= x[i], yy= y[i];
00167     decode (xx, yy);
00168     pnt[i].x= xx;
00169     pnt[i].y= yy;
00170   }
00171   XDrawLines (dpy, win, gc, pnt, n, CoordModeOrigin);
00172   STACK_DELETE_ARRAY (pnt);
00173 }
00174 
00175 void
00176 x_drawable_rep::clear (SI x1, SI y1, SI x2, SI y2) {
00177   x1= max (x1, cx1-ox); y1= max (y1, cy1-oy);
00178   x2= min (x2, cx2-ox); y2= min (y2, cy2-oy);
00179   // outer_round (x1, y1, x2, y2); might still be needed somewhere
00180   decode (x1, y1);
00181   decode (x2, y2);
00182   if ((x1>=x2) || (y1<=y2)) return;
00183   XSetForeground (dpy, gc, CONVERT (cur_bg));
00184   XFillRectangle (dpy, win, gc, x1, y2, x2-x1, y1-y2);
00185   XSetForeground (dpy, gc, CONVERT (blend (cur_fg, cur_bg)));
00186 }
00187 
00188 void
00189 x_drawable_rep::fill (SI x1, SI y1, SI x2, SI y2) {
00190   if ((x2>x1) && ((x2-x1)<pixel)) {
00191     SI d= pixel-(x2-x1);
00192     x1 -= (d>>1);
00193     x2 += ((d+1)>>1);
00194   }
00195   if ((y2>y1) && ((y2-y1)<pixel)) {
00196     SI d= pixel-(y2-y1);
00197     y1 -= (d>>1);
00198     y2 += ((d+1)>>1);
00199   }
00200 
00201   x1= max (x1, cx1-ox); y1= max (y1, cy1-oy);
00202   x2= min (x2, cx2-ox); y2= min (y2, cy2-oy);
00203   // outer_round (x1, y1, x2, y2); might still be needed somewhere
00204   if ((x1>=x2) || (y1>=y2)) return;
00205 
00206   decode (x1, y1);
00207   decode (x2, y2);
00208   XFillRectangle (dpy, win, gc, x1, y2, x2-x1, y1-y2);
00209 }
00210 
00211 void
00212 x_drawable_rep::arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
00213   if ((x1>=x2) || (y1>=y2)) return;
00214   decode (x1, y1);
00215   decode (x2, y2);
00216   XDrawArc (dpy, win, gc, x1, y2, x2-x1, y1-y2, alpha, delta);
00217 }
00218 
00219 void
00220 x_drawable_rep::fill_arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
00221   if ((x1>=x2) || (y1>=y2)) return;
00222   decode (x1, y1);
00223   decode (x2, y2);
00224   XFillArc (dpy, win, gc, x1, y2, x2-x1, y1-y2, alpha, delta);
00225 }
00226 
00227 void
00228 x_drawable_rep::polygon (array<SI> x, array<SI> y, bool convex) {
00229   int i, n= N(x);
00230   if ((N(y) != n) || (n<1)) return;
00231   STACK_NEW_ARRAY (pnt, XPoint, n);
00232   for (i=0; i<n; i++) {
00233     SI xx= x[i], yy= y[i];
00234     decode (xx, yy);
00235     pnt[i].x= xx;
00236     pnt[i].y= yy;
00237   }
00238   XFillPolygon (dpy, win, gc, pnt, n, convex?Convex:Complex, CoordModeOrigin);
00239   STACK_DELETE_ARRAY (pnt);
00240 }
00241 
00242 /******************************************************************************
00243 * Setting up and displaying xpm pixmaps
00244 ******************************************************************************/
00245 
00246 void
00247 x_drawable_rep::xpm_initialize (url file_name) {
00248   tree t= xpm_load (file_name);
00249 
00250   // get main info
00251   int ok, i=0, j, k, w, h, c, b, x, y;
00252   string s= as_string (t[0]);
00253   skip_spaces (s, i);
00254   ok= read_int (s, i, w);
00255   skip_spaces (s, i);
00256   ok= read_int (s, i, h) && ok;
00257   skip_spaces (s, i);
00258   ok= read_int (s, i, c) && ok;
00259   skip_spaces (s, i);
00260   ok= read_int (s, i, b) && ok;
00261   if ((!ok) || (N(t)<(c+1)) || (c<=0)) {
00262     cerr << "file_name= " << file_name << "\n";
00263     FAILED ("invalid xpm");
00264   }
00265 
00266   // setup colors
00267   string first_name;
00268   hashmap<string,int> pmcs(0);
00269   hashmap<string,int> bmcs(1);
00270   for (k=0; k<c; k++) {
00271     string s   = as_string (t[k+1]);
00272     string name= "";
00273     string def = "none";
00274     if (N(s)<b) i=N(s);
00275     else { name= s(0,b); i=b; }
00276     if (k==0) first_name= name;
00277 
00278     skip_spaces (s, i);
00279     if ((i<N(s)) && (s[i]=='s')) {
00280       i++;
00281       skip_spaces (s, i);
00282       while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
00283       skip_spaces (s, i);
00284     }
00285     if ((i<N(s)) && (s[i]=='c')) {
00286       i++;
00287       skip_spaces (s, i);
00288       j=i;
00289       while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
00290       def= locase_all (s (j, i));
00291     }
00292     if (def == "none") {
00293       bmcs(name)= 0;
00294       def= "lightgrey";
00295     }
00296     else bmcs(name)= 1;
00297  /* FIXME: to avoid code duplication, replace this code by
00298       a call to xpm_colors(), plus the appropriate code to
00299       fill bmcs() & set first_name. */
00300 
00301     char* _def= as_charp (def);
00302     XColor exact, closest;
00303     XLookupColor (gui->dpy, gui->cols, _def, &exact, &closest);
00304     if (!reverse_colors && XAllocColor (gui->dpy, gui->cols, &exact))
00305       pmcs(name)= exact.pixel;
00306     else if (!reverse_colors && XAllocColor (gui->dpy, gui->cols, &closest))
00307       pmcs(name)= closest.pixel;
00308     else {
00309       color myc= rgb_color (exact.red/256, exact.green/256, exact.blue/256);
00310       pmcs(name)= CONVERT (myc);
00311     }
00312     tm_delete_array (_def);
00313   }
00314 
00315   // setup bitmap and pixmap
00316   Pixmap pm= XCreatePixmap (gui->dpy, gui->root, w, h, gui->depth);
00317   int byte_width= ((w-1)>>3)+1;
00318   char* data= tm_new_array<char> (byte_width * h);
00319   for (i=0; i<(byte_width * h); i++) data[i]=0;
00320   for (y=0; y<h; y++) {
00321     if (N(t)< (y+c+1)) s= "";
00322     else s= as_string (t[y+c+1]);
00323     for (x=0; x<w; x++) {
00324       string name;
00325       int bit;
00326       if (N(s)<(b*(x+1))) name= first_name;
00327       else name= s (b*x, b*(x+1));
00328       int bmc= bmcs[name];
00329       int pmc= pmcs[name];
00330       if (!bmcs->contains (name)) bmc= bmcs[first_name];
00331       if (!pmcs->contains (name)) pmc= pmcs[first_name];
00332       XSetForeground (gui->dpy, gui->pixmap_gc, pmc);
00333       XDrawPoint (gui->dpy, (Drawable) pm, gui->pixmap_gc, x, y);
00334       bit= y*byte_width + (x>>3);
00335       if (bmc!=0) data[bit]= data[bit] | (1<<(x&7));      
00336     }
00337   }
00338   Pixmap bm= XCreateBitmapFromData (gui->dpy, gui->root, data, w, h);
00339   gui->xpm_pixmap (as_string (file_name))= (int) pm;
00340   gui->xpm_bitmap (as_string (file_name))= (int) bm;
00341   tm_delete_array (data);
00342 }
00343 
00344 extern bool char_clip;
00345 
00346 void
00347 x_drawable_rep::xpm (url file_name, SI x, SI y) {
00348   y -= pixel; // counter balance shift in draw_clipped
00349   if (!gui->xpm_pixmap->contains (as_string (file_name)))
00350     xpm_initialize (file_name);
00351   ASSERT (sfactor == 1, "shrinking factor should be 1");
00352   int w, h;
00353   xpm_size (file_name, w, h);
00354   Pixmap bm= (Pixmap) gui->xpm_bitmap [as_string (file_name)];
00355   Pixmap pm= (Pixmap) gui->xpm_pixmap [as_string (file_name)];
00356   int old_clip= char_clip;
00357   char_clip= true;
00358   draw_clipped (pm, bm, w, h, x, y);
00359   char_clip=old_clip;
00360 }
00361 
00362 /******************************************************************************
00363 * Invocation of ghostscript
00364 ******************************************************************************/
00365 
00366 static time_t cache_image_last_gc = 0;
00367 static int    cache_image_tot_size= 0;
00368 static int    cache_image_max_size= 10000;
00369 static hashmap<tree,Pixmap> cache_image (0);
00370 static hashmap<tree,int> cache_image_w (0);
00371 static hashmap<tree,int> cache_image_h (0);
00372 static hashmap<tree,int> cache_image_time (0);
00373 static hashmap<tree,int> cache_image_nr (0);
00374 
00375 void
00376 x_drawable_rep::image (
00377   url u, SI w, SI h, SI x, SI y,
00378   double cx1, double cy1, double cx2, double cy2,
00379   int alpha)
00380 {
00381   // Given an image of original size (W, H),
00382   // we display the part (cx1 * W, xy1 * H, cx2 * W, cy2 * H)
00383   // at position (x, y) in a rectangle of size (w, h)
00384   (void) alpha; // FIXME
00385 
00386   w= w/pixel; h= h/pixel;
00387   decode (x, y);
00388 
00389   if (gui->gswindow == NULL) {
00390     SI max_w= gui->screen_width  * PIXEL;
00391     SI max_h= gui->screen_height * PIXEL;
00392     //widget dummy= text_widget (0, "ghostscript window");
00393     if (ghostscript_bugged ()) {
00394       max_w *= 2;
00395       max_h *= 2;
00396       //dummy= glue_widget (false, false, max_w, max_h);
00397     }
00398     widget dummy = glue_widget (false, false, max_w, max_h);
00399     widget win   = plain_window_widget (dummy, "Ghostscript");
00400     gui->gswindow= get_x_window (win);
00401     //gui->gswindow= tm_new<x_window_rep> (dummy, gui, "ghostscript", 0, 0);
00402     //gui->gswindow= tm_new<x_window_rep> (dummy, gui, "ghostscript",
00403     //max_w, max_h, max_w, max_h, max_w, max_h);
00404     nr_windows--; // the dummy window should not be counted
00405   }
00406 
00407   Pixmap pm;
00408   tree lookup= tuple (u->t);
00409   lookup << as_string (w ) << as_string (h )
00410         << as_string (cx1) << as_string (cy1)
00411         << as_string (cx2) << as_string (cy2);
00412   if (cache_image->contains (lookup)) pm= (Pixmap) cache_image [lookup];
00413   else {
00414     // rendering
00415     Window gs_win= gui->gswindow->win;
00416 
00417     // XCreatePixmap does not allow for zero sized images.
00418     // This fixes bug #10425.
00419     w = (w==0 ? 1 : w);
00420     h = (h==0 ? 1 : h);
00421 
00422     pm= XCreatePixmap (gui->dpy, gs_win, w, h, gui->depth);
00423     if (imlib2_supports (u))
00424       imlib2_display (dpy, pm, u, w, h, cx1, cy1, cx2, cy2);
00425     else {
00426       //XSetForeground (dpy, gc, white);
00427       //XFillRectangle (dpy, pm, gc, 0, 0, w, h);
00428       ghostscript_run (dpy, gs_win, pm, u, w, h, cx1, cy1, cx2, cy2);
00429     }
00430 
00431     // caching
00432     if (N(cache_image_nr) == 0) cache_image_last_gc= texmacs_time ();
00433     cache_image      (lookup)= (int) pm;
00434     cache_image_w    (lookup)= w;
00435     cache_image_h    (lookup)= h;
00436     cache_image_time (lookup)= texmacs_time ();
00437     cache_image_nr   (lookup)= cache_image_nr [lookup] + 1;
00438     cache_image_tot_size += w*h;
00439     if (cache_image_tot_size > cache_image_max_size) {
00440       gui->image_auto_gc ();
00441       if (cache_image_tot_size > cache_image_max_size)
00442        cache_image_max_size= cache_image_tot_size << 1;
00443     }
00444   }
00445 
00446   XCopyArea (dpy, (Drawable) pm, win, gc, 0, 0, w, h, x, y-h);
00447 }
00448 
00449 void
00450 x_gui_rep::image_auto_gc () {
00451   time_t time= texmacs_time ();
00452   if (time-cache_image_last_gc <= 300000) return;
00453   cache_image_last_gc= time;
00454   if (DEBUG_AUTO)
00455     cout << "TeXmacs] Launching garbage collection for unused pictures\n";
00456 
00457   iterator<tree> it= iterate (cache_image);
00458   while (it->busy()) {
00459     tree lookup= it->next();
00460     int diff= time- cache_image_time [lookup];
00461     int fact= cache_image_nr [lookup];
00462     fact= fact * fact * fact;
00463     if (cache_image_w [lookup] * cache_image_h [lookup] < 400) fact= fact * 5;
00464     if (cache_image_w [lookup] * cache_image_h [lookup] < 6400) fact= fact * 5;
00465     if (diff/fact > 60000) {
00466       Pixmap pm= (Pixmap) cache_image [lookup];
00467       XFreePixmap (dpy, pm);
00468       cache_image->reset (lookup);
00469       cache_image_w->reset (lookup);
00470       cache_image_h->reset (lookup);
00471       cache_image_time->reset (lookup);
00472       ps_bbox->reset (lookup[0]);
00473     }
00474   }
00475 }
00476 
00477 void
00478 x_gui_rep::image_gc (string name) {
00479   (void) name;
00480   cache_image_last_gc= texmacs_time ();
00481   iterator<tree> it= iterate (cache_image);
00482   while (it->busy()) {
00483     tree lookup= it->next();
00484     if (!is_ramdisc (as_url (lookup[0]))) {
00485       Pixmap pm= (Pixmap) cache_image [lookup];
00486       XFreePixmap (dpy, pm);
00487       cache_image->reset (lookup);
00488       cache_image_w->reset (lookup);
00489       cache_image_h->reset (lookup);
00490       cache_image_time->reset (lookup);
00491       cache_image_nr->reset (lookup);
00492       ps_bbox->reset (lookup[0]);
00493     }
00494   }
00495 }
00496 
00497 void
00498 image_gc (string name) {
00499   the_gui->image_gc (name);
00500 }