Back to index

texmacs  1.0.7.15
aqua_renderer.mm
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : aqua_renderer.mm
00004 * DESCRIPTION: Cocoa drawing interface class
00005 * COPYRIGHT  : (C) 2006 Massimiliano Gubinelli
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 "mac_cocoa.h"
00013 #include "aqua_renderer.h"
00014 #include "analyze.hpp"
00015 #include "image_files.hpp"
00016 #include "file.hpp"
00017 
00018 #include "aqua_utilities.h"
00019 #include "MacOS/mac_images.h"
00020 
00021 
00022 
00023 /******************************************************************************
00024  * Cocoa images
00025  ******************************************************************************/
00026 
00027 struct aqua_image_rep: concrete_struct {
00028        NSImage* img;
00029        SI xo,yo;
00030        int w,h;
00031        aqua_image_rep (NSImage* img2, SI xo2, SI yo2, int w2, int h2) :
00032   img (img2), xo (xo2), yo (yo2), w (w2), h (h2) { [img retain]; };
00033        ~aqua_image_rep()  {  [img release]; };
00034        friend class aqua_image;
00035 };
00036 
00037 class aqua_image {
00038        CONCRETE_NULL(aqua_image);
00039   aqua_image (NSImage* img2, SI xo2, SI yo2, int w2, int h2):
00040   rep (tm_new <aqua_image_rep> (img2, xo2, yo2, w2, h2)) {}    
00041 };
00042 
00043 CONCRETE_NULL_CODE(aqua_image);
00044 
00045 /******************************************************************************
00046  * CG images
00047  ******************************************************************************/
00048 
00049 struct cg_image_rep: concrete_struct {
00050        CGImageRef img;
00051        SI xo,yo;
00052        int w,h;
00053        cg_image_rep (CGImageRef img2, SI xo2, SI yo2, int w2, int h2) :
00054     img (img2), xo (xo2), yo (yo2), w (w2), h (h2) { CGImageRetain(img); };
00055        ~cg_image_rep()  {  CGImageRelease(img); };
00056        friend class cg_image;
00057 };
00058 
00059 class cg_image {
00060        CONCRETE_NULL(cg_image);
00061        cg_image (CGImageRef img2, SI xo2, SI yo2, int w2, int h2):
00062     rep (tm_new <cg_image_rep> (img2, xo2, yo2, w2, h2)) {}    
00063 };
00064 
00065 CONCRETE_NULL_CODE(cg_image);
00066 
00067 
00068 /******************************************************************************
00069  * Global support variables for all aqua_renderers
00070  ******************************************************************************/
00071 
00072 
00073 static hashmap<basic_character,cg_image> character_image;  // bitmaps of all characters
00074 static hashmap<string,aqua_image> images; 
00075 
00076 
00077 
00078 /******************************************************************************
00079  * aqua_renderer
00080  ******************************************************************************/
00081 
00082 void 
00083 aqua_set_color (color col) {
00084   int r, g, b, a;
00085   get_rgb_color (col, r, g, b, a);
00086   [[NSColor colorWithDeviceRed: r/255.0 green:g/255.0 blue:b/255.0 alpha:a/255.0] set];
00087 }
00088 
00089 aqua_renderer_rep::aqua_renderer_rep (int w2, int h2) :
00090   basic_renderer_rep(w2,h2), context(NULL)
00091 {
00092 }
00093 
00094 aqua_renderer_rep::~aqua_renderer_rep () {
00095   if (context) end();
00096 } ;
00097 
00098 void 
00099 aqua_renderer_rep::begin (void * c) { 
00100   context = (NSGraphicsContext*)c; 
00101   [context retain];
00102 //  CGContextBeginPage(context, NULL);
00103 }
00104 
00105 void 
00106 aqua_renderer_rep::end () { 
00107 //  CGContextEndPage(context);
00108   [context release];
00109 //  CGContextRelease(context); 
00110   context = NULL;  
00111 }
00112 
00113 void
00114 aqua_renderer_rep::set_color (color c) {
00115   basic_renderer_rep::set_color(c);
00116   aqua_set_color(cur_fg);
00117 }
00118 
00119 
00120 void
00121 aqua_renderer_rep::set_line_style (SI lw, int type, bool round) { (void) type;
00122   if (lw <= pixel)
00123   {
00124     [NSBezierPath setDefaultLineWidth:1.0];
00125   }
00126   else
00127   {
00128     [NSBezierPath setDefaultLineWidth:(lw+thicken)/(1.0*pixel)];
00129   }
00130   [NSBezierPath setDefaultLineCapStyle:(round ? NSRoundLineCapStyle : NSButtLineCapStyle)];
00131   [NSBezierPath setDefaultLineJoinStyle: NSRoundLineJoinStyle];
00132 }
00133 
00134 void
00135 aqua_renderer_rep::line (SI x1, SI y1, SI x2, SI y2) {
00136   decode (x1, y1);
00137   decode (x2, y2);
00138  // y1--; y2--; // top-left origin to bottom-left origin conversion
00139   [NSBezierPath strokeLineFromPoint:NSMakePoint(x1,y1) toPoint:NSMakePoint(x2,y2)];
00140 }
00141 
00142 
00143 void
00144 aqua_renderer_rep::lines (array<SI> x, array<SI> y) {
00145   int i, n= N(x);
00146   if ((N(y) != n) || (n<1)) return;
00147   STACK_NEW_ARRAY (pnt, NSPoint, n);
00148   for (i=0; i<n; i++) {
00149     SI xx= x[i], yy= y[i];
00150     decode (xx, yy);
00151     pnt[i].x= xx;
00152     pnt[i].y= yy;
00153     if (i>0) [NSBezierPath strokeLineFromPoint:pnt[i-1] toPoint:pnt[i]]; // FIX: hack
00154   }
00155  // XDrawLines (dpy, win, gc, pnt, n, CoordModeOrigin);
00156   STACK_DELETE_ARRAY (pnt);
00157 }
00158 
00159 
00160 void
00161 aqua_renderer_rep::clear (SI x1, SI y1, SI x2, SI y2) {
00162   x1= max (x1, cx1-ox); y1= max (y1, cy1-oy);
00163   x2= min (x2, cx2-ox); y2= min (y2, cy2-oy);
00164   // outer_round (x1, y1, x2, y2); might still be needed somewhere
00165   decode (x1, y1);
00166   decode (x2, y2);
00167          if ((x1>=x2) || (y1<=y2)) return;
00168        NSRect rect = NSMakeRect(x1,y2,x2-x1,y1-y2);
00169   aqua_set_color (cur_bg);
00170   [NSBezierPath fillRect:rect];
00171   aqua_set_color (cur_fg);
00172 }
00173 
00174 void
00175 aqua_renderer_rep::fill (SI x1, SI y1, SI x2, SI y2) {
00176   if ((x2>x1) && ((x2-x1)<pixel)) {
00177     SI d= pixel-(x2-x1);
00178     x1 -= (d>>1);
00179     x2 += ((d+1)>>1);
00180   }
00181   if ((y2>y1) && ((y2-y1)<pixel)) {
00182     SI d= pixel-(y2-y1);
00183     y1 -= (d>>1);
00184     y2 += ((d+1)>>1);
00185   }
00186   
00187   x1= max (x1, cx1-ox); y1= max (y1, cy1-oy);
00188   x2= min (x2, cx2-ox); y2= min (y2, cy2-oy);
00189   // outer_round (x1, y1, x2, y2); might still be needed somewhere
00190   if ((x1>=x2) || (y1>=y2)) return;
00191   
00192   decode (x1, y1);
00193   decode (x2, y2);
00194   [NSBezierPath fillRect:NSMakeRect(x1,y2,x2-x1,y1-y2)];
00195 }
00196 
00197 void
00198 aqua_renderer_rep::arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
00199   if ((x1>=x2) || (y1>=y2)) return;
00200   decode (x1, y1);
00201   decode (x2, y2);
00202   //FIXME: XDrawArc (dpy, win, gc, x1, y2, x2-x1, y1-y2, alpha, delta);
00203 }
00204 
00205 void
00206 aqua_renderer_rep::fill_arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
00207   if ((x1>=x2) || (y1>=y2)) return;
00208   decode (x1, y1);
00209   decode (x2, y2);
00210   //FIXME: XFillArc (dpy, win, gc, x1, y2, x2-x1, y1-y2, alpha, delta);
00211 }
00212 
00213 void
00214 aqua_renderer_rep::polygon (array<SI> x, array<SI> y, bool convex) {  
00215   int i, n= N(x);
00216   if ((N(y) != n) || (n<1)) return;
00217   STACK_NEW_ARRAY (pnt, NSPoint, n);
00218   for (i=0; i<n; i++) {
00219     SI xx= x[i], yy= y[i];
00220     decode (xx, yy);
00221     pnt[i] = NSMakePoint(xx,yy);
00222   }
00223   
00224   NSBezierPath *path = [NSBezierPath bezierPath];
00225   [path  appendBezierPathWithPoints:pnt count:n];
00226   [path setWindingRule:(convex? NSEvenOddWindingRule : NSNonZeroWindingRule)];
00227   [path fill];
00228   STACK_DELETE_ARRAY (pnt);
00229 }
00230 
00231 /******************************************************************************
00232  * Image rendering
00233  ******************************************************************************/
00234 struct aqua_cache_image_rep: cache_image_element_rep {
00235        aqua_cache_image_rep (int w2, int h2, time_t time2, NSImage *ptr2) :
00236   cache_image_element_rep(w2,h2,time2,ptr2) {  [(NSImage*)ptr retain]; };
00237        virtual ~aqua_cache_image_rep() {   [(NSImage*)ptr release]; };
00238 };
00239 
00240 
00241 void
00242 aqua_renderer_rep::image (url u, SI w, SI h, SI x, SI y,
00243                           double cx1, double cy1, double cx2, double cy2,
00244                           int alpha)
00245 {
00246   // Given an image of original size (W, H),
00247   // we display the part (cx1 * W, xy1 * H, cx2 * W, cy2 * H)
00248   // at position (x, y) in a rectangle of size (w, h)
00249   
00250   // if (DEBUG_EVENTS) cout << "cg_renderer_rep::image " << as_string(u) << LF;
00251   (void) alpha; // FIXME
00252   
00253   w= w/pixel; h= h/pixel;
00254   decode (x, y);
00255   
00256   //painter.setRenderHints (0);
00257   //painter.drawRect (QRect (x, y-h, w, h));
00258   
00259   NSImage *pm = NULL;
00260   tree lookup= tuple (u->t);
00261   lookup << as_string (w ) << as_string (h )
00262   << as_string (cx1) << as_string (cy1)
00263   << as_string (cx2) << as_string (cy2) << "cg-image" ;
00264   cache_image_element ci = get_image_cache(lookup);
00265   if (!is_nil(ci)) {
00266     pm = static_cast<NSImage*> (ci->ptr);
00267   } else {
00268          if (suffix (u) == "png") {
00269       // rendering
00270       string suu = as_string (u);
00271       // cout << suu << LF;
00272       pm = [[NSImage alloc] initWithContentsOfFile:to_nsstring(suu)];
00273          } else if (suffix (u) == "ps" ||
00274                suffix (u) == "eps" ||
00275                suffix (u) == "pdf") {
00276       url temp= url_temp (".png");
00277       mac_image_to_png (u, temp, w, h);
00278 //      system ("convert", u, temp);
00279       string suu = as_string (temp);
00280       pm = [[NSImage alloc] initWithContentsOfFile:to_nsstring(suu)];
00281       remove (temp);
00282     }
00283     
00284     if (pm == NULL ) {
00285       cout << "TeXmacs] warning: cannot render " << as_string (u) << "\n";
00286       return;
00287     }
00288     // caching
00289     ci = tm_new <aqua_cache_image_rep> (w,h, texmacs_time(), pm);
00290     set_image_cache(lookup, ci);
00291     (ci->nr)++;
00292   }
00293   
00294   NSSize isz = [pm size];
00295   [pm setFlipped:YES];
00296 //  [pm drawAtPoint:NSMakePoint(x,y) fromRect:NSMakeRect(0,0,w,h) operation:NSCompositeSourceAtop fraction:1.0];
00297   [pm drawInRect:NSMakeRect(x,y-h,w,h) fromRect:NSMakeRect(0,0,isz.width,isz.height) operation:NSCompositeSourceAtop fraction:1.0];
00298 }
00299 
00300 
00301 void
00302 aqua_renderer_rep::draw_clipped (NSImage *im, int w, int h, SI x, SI y) {
00303   decode (x , y );
00304   y--; // top-left origin to bottom-left origin conversion
00305   [im drawAtPoint:NSMakePoint(x,y) fromRect:NSMakeRect(0,0,w,h) operation:NSCompositeSourceAtop fraction:1.0];
00306 }  
00307 
00308 
00309 
00310 static CGContextRef 
00311 MyCreateBitmapContext (int pixelsWide, int pixelsHigh) {
00312     int bitmapBytesPerRow   = (pixelsWide * 4);
00313     int bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);       
00314     CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
00315     void *bitmapData = malloc( bitmapByteCount );
00316     if (bitmapData == NULL) {
00317         //fprintf (stderr, "Memory not allocated!");
00318         return NULL;
00319     }
00320     CGContextRef context = CGBitmapContextCreate (bitmapData, pixelsWide,    pixelsHigh,   8,
00321                                                   bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
00322     if (context == NULL) {
00323         free (bitmapData);
00324               // fprintf (stderr, "Context not created!");
00325         return NULL;
00326     }
00327     CGColorSpaceRelease (colorSpace);
00328     return context;
00329 }
00330 
00331 
00332 void
00333 aqua_renderer_rep::draw (int c, font_glyphs fng, SI x, SI y) {
00334        // get the pixmap
00335        basic_character xc (c, fng, sfactor, 0, 0);
00336        cg_image mi = character_image [xc];
00337        if (is_nil(mi)) {
00338               SI xo, yo;
00339               glyph pre_gl= fng->get (c); if (is_nil (pre_gl)) return;
00340               glyph gl= shrink (pre_gl, sfactor, sfactor, xo, yo);
00341               int i, j, w= gl->width, h= gl->height;
00342               CGImageRef im = NULL;
00343               {
00344                      CGContextRef ic = MyCreateBitmapContext(w,h);
00345                      int nr_cols= sfactor*sfactor;
00346                      if (nr_cols >= 64) nr_cols= 64;
00347                      //CGContextSetShouldAntialias(ic,true);
00348                      CGContextSetBlendMode(ic,kCGBlendModeCopy);
00349                      //CGContextSetRGBFillColor(ic,1.0,1.0,1.0,0.0);
00350                      //CGContextFillRect(ic,CGRectMake(0,0,w,h));
00351                      
00352                      for (j=0; j<h; j++)
00353                             for (i=0; i<w; i++) {
00354                                    int col = gl->get_x (i, j);
00355                                    CGContextSetRGBFillColor(ic, 0.0,0.0,0.0,  ((255*col)/(nr_cols+1))/255.0);
00356                                    CGContextFillRect(ic,CGRectMake(i,j,1,1));
00357                             }
00358                      im = CGBitmapContextCreateImage (ic);
00359                      CGContextRelease (ic);
00360               }
00361               cg_image mi2 (im, xo, yo, w, h);
00362               mi = mi2;
00363               CGImageRelease(im); // cg_image retains im
00364               character_image (xc)= mi;
00365        }
00366        
00367        // draw the character
00368        {
00369     CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
00370 
00371               (void) w; (void) h;
00372               int x1= x- mi->xo*sfactor;
00373               int y1=  y+ mi->yo*sfactor;
00374               decode (x1, y1);
00375               y1--; // top-left origin to bottom-left origin conversion
00376               CGRect r = CGRectMake(x1,y1,mi->w,mi->h);
00377               CGContextSetShouldAntialias (cgc, true);
00378               CGContextSaveGState (cgc);
00379               //  cg_set_color (context, cur_fg);
00380               CGContextClipToMask (cgc, r, mi->img); 
00381               CGContextFillRect (cgc, r);
00382               CGContextRestoreGState (cgc);
00383        }  
00384 }
00385 #if 0
00386 void aqua_renderer_rep::draw (int c, font_glyphs fng, SI x, SI y) {
00387   // get the pixmap
00388   basic_character xc (c, fng, sfactor, 0, 0);
00389   cg_image mi = character_image [xc];
00390   if (is_nil(mi)) {
00391     // cout << "CACHING:" << c << "\n" ;
00392     SI xo, yo;
00393     glyph pre_gl= fng->get (c); if (is_nil (pre_gl)) return;
00394     glyph gl= shrink (pre_gl, sfactor, sfactor, xo, yo);
00395     int i, j, w= gl->width, h= gl->height;
00396     NSImage *im = [[NSImage alloc] initWithSize:NSMakeSize(w,h)];
00397     int nr_cols= sfactor*sfactor;
00398     if (nr_cols >= 64) nr_cols= 64;
00399 
00400     [im lockFocus];
00401     for (j=0; j<h; j++)
00402       for (i=0; i<w; i++) {
00403         int col = gl->get_x (i, j);
00404         [[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha: ((255*col)/(nr_cols+1))/255.0] set]; 
00405         [NSBezierPath fillRect:NSMakeRect(i,j,1,1)];
00406       }
00407     [im unlockFocus];
00408     
00409     aqua_image mi2(im, xo, yo, w, h );
00410        mi = mi2;
00411     [im release]; // aqua_image retains im
00412     character_image (xc)= mi;
00413     // FIXME: we must release the image at some point (this should be ok now, see aqua_image)
00414   }
00415   
00416   // draw the character
00417   {
00418     CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
00419     (void) w; (void) h;
00420     int x1= x- mi->xo*sfactor;
00421     int y1=  y+ mi->yo*sfactor;
00422     decode (x1, y1);
00423     y1--; // top-left origin to bottom-left origin conversion
00424     CGRect r = CGRectMake(x1,y1,mi->w,mi->h);
00425     CGContextSetShouldAntialias (cgc, true);
00426     CGContextSaveGState (cgc);
00427     //  aqua_set_color (context, cur_fg);
00428     CGContextClipToMask (cgc, r, (CGImage*)(mi->img)); 
00429     CGContextFillRect (cgc, r);
00430     CGContextRestoreGState (cgc);
00431   }  
00432 
00433   
00434   // draw the character
00435 //  draw_clipped (mi->img, mi->w, mi->h,
00436  //               x- mi->xo*sfactor, y+ mi->yo*sfactor);
00437 }
00438 #endif
00439 
00440 /******************************************************************************
00441 * Setting up and displaying xpm pixmaps
00442 ******************************************************************************/
00443 
00444 NSImage* xpm_init(url file_name)
00445 {
00446   tree t= xpm_load (file_name);
00447   
00448   // get main info
00449   int ok, i=0, j, k, w, h, c, b, x, y;
00450   string s= as_string (t[0]);
00451   skip_spaces (s, i);
00452   ok= read_int (s, i, w);
00453   skip_spaces (s, i);
00454   ok= read_int (s, i, h) && ok;
00455   skip_spaces (s, i);
00456   ok= read_int (s, i, c) && ok;
00457   skip_spaces (s, i);
00458   ok= read_int (s, i, b) && ok;
00459   if ((!ok) || (N(t)<(c+1)) || (c<=0)) {
00460     cerr << "File name= " << file_name << "\n";
00461     FAILED ("invalid xpm");
00462   }
00463   
00464   // setup colors
00465   string first_name;
00466   hashmap<string,color> pmcs;
00467   for (k=0; k<c; k++) {
00468     string s   = as_string (t[k+1]);
00469     string name= "";
00470     string def = "none";
00471     if (N(s)<b) i=N(s);
00472     else { name= s(0,b); i=b; }
00473     if (k==0) first_name= name;
00474     
00475     skip_spaces (s, i);
00476     if ((i<N(s)) && (s[i]=='s')) {
00477       i++;
00478       skip_spaces (s, i);
00479       while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
00480       skip_spaces (s, i);
00481     }
00482     if ((i<N(s)) && (s[i]=='c')) {
00483       i++;
00484       skip_spaces (s, i);
00485       j=i;
00486       while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
00487       def= locase_all (s (j, i));
00488     }
00489 
00490               pmcs(name)= xpm_to_color(def);
00491   }
00492   NSImage *im = [[NSImage alloc] initWithSize:NSMakeSize(w,h)];
00493        [im setFlipped:YES];
00494   [im lockFocus];
00495   [[NSGraphicsContext currentContext] setCompositingOperation: NSCompositeCopy];
00496 
00497   // setup pixmap
00498   for (y=0; y<h; y++) {
00499     if (N(t)< (y+c+1)) s= "";
00500     else s= as_string (t[y+c+1]);
00501     for (x=0; x<w; x++) {
00502       string name;
00503       if (N(s)<(b*(x+1))) name= first_name;
00504       else name= s (b*x, b*(x+1));
00505       if ((name == first_name) || !(pmcs->contains (name)))
00506         [[NSColor colorWithDeviceWhite:1.0 alpha:0.0] set] ;      
00507       else {
00508       color col = pmcs[name];
00509       aqua_set_color (col);
00510         }
00511          [NSBezierPath fillRect:NSMakeRect(x,y,1,1)];
00512     }
00513   }
00514   [im unlockFocus];
00515   return im;
00516 }
00517 
00518 
00519 extern int char_clip;
00520 
00521 NSImage *
00522 aqua_renderer_rep::xpm_image(url file_name)
00523 { 
00524        NSImage *image = nil;
00525   aqua_image mi = images [as_string(file_name)];
00526   if (is_nil(mi)) {    
00527               image = xpm_init(file_name);
00528     int w, h;
00529     NSSize imgSize = [image size];
00530     w = imgSize.width; h = imgSize.height;
00531               aqua_image mi2(image,0,0,w,h);
00532               mi = mi2;
00533               images(as_string(file_name)) = mi2;       
00534     [image release];
00535   }  
00536   else image = mi->img;
00537        return image;
00538 }
00539 
00540 void aqua_renderer_rep::xpm (url file_name, SI x, SI y) {
00541   y -= pixel; // counter balance shift in draw_clipped
00542   
00543  // char *chstr = as_charp(as_string(file_name));
00544 //  NSString *name = [NSString stringWithCString:chstr];
00545  // delete [] chstr;
00546 //  name = [[name stringByDeletingPathExtension] stringByAppendingPathExtension:@"png"];
00548   NSImage *image = xpm_image(file_name);
00549   
00550   ASSERT (sfactor == 1, "shrinking factor should be 1");
00551   int w, h;
00552   NSSize imgSize = [image size];
00553   w = imgSize.width; h = imgSize.height;
00554 
00555 //  [(NSImageRep*)[[image representations] objectAtIndex:0]  drawAtPoint:NSMakePoint(x,y)];
00556   
00557   int old_clip= char_clip;
00558   char_clip= true;
00559   draw_clipped (image, w, h, x, y);
00560   char_clip=old_clip;
00561 }
00562 
00563 
00564 /******************************************************************************
00565  * main cocoa renderer
00566  ******************************************************************************/
00567 
00568 
00569 aqua_renderer_rep*
00570 the_aqua_renderer () {
00571   static aqua_renderer_rep* the_renderer= NULL;
00572        if (!the_renderer) the_renderer= tm_new <aqua_renderer_rep> ();
00573        return the_renderer;
00574 }
00575