/TeXmacs-1.0.7.11-src/src/Plugins/MacOS/cg_renderer.cpp
C++ | 618 lines | 464 code | 82 blank | 72 comment | 92 complexity | 5ffbfed37c018d7dcd04fb5fbcce9175 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, MPL-2.0-no-copyleft-exception
1
2/******************************************************************************
3* MODULE : cg_renderer.cpp
4* DESCRIPTION: CoreGraphics drawing interface class
5* COPYRIGHT : (C) 2008 Massimiliano Gubinelli
6*******************************************************************************
7* This software falls under the GNU general public license and comes WITHOUT
8* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
9* If you don't have this file, write to the Free Software Foundation, Inc.,
10* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
11******************************************************************************/
12
13#include "cg_renderer.hpp"
14#include "analyze.hpp"
15#include "image_files.hpp"
16#include "file.hpp"
17#include "iterator.hpp"
18#include "gui.hpp" // for INTERRUPT_EVENT, INTERRUPTED_EVENT
19#include "font.hpp" // for the definition of font
20
21#include "Freetype/tt_file.hpp" // tt_font_find
22
23#include "mac_images.h"
24
25/******************************************************************************
26* CG images
27******************************************************************************/
28
29struct cg_image_rep: concrete_struct {
30 CGImageRef img;
31 SI xo,yo;
32 int w,h;
33 cg_image_rep (CGImageRef img2, SI xo2, SI yo2, int w2, int h2) :
34 img (img2), xo (xo2), yo (yo2), w (w2), h (h2) { CGImageRetain(img); };
35 ~cg_image_rep() { CGImageRelease(img); };
36 friend class cg_image;
37};
38
39class cg_image {
40 CONCRETE_NULL(cg_image);
41 cg_image (CGImageRef img2, SI xo2, SI yo2, int w2, int h2):
42 rep (tm_new <cg_image_rep> (img2, xo2, yo2, w2, h2)) {}
43};
44
45CONCRETE_NULL_CODE(cg_image);
46
47/******************************************************************************
48 * Global support variables for all cg_renderers
49 ******************************************************************************/
50
51
52static hashmap<basic_character,cg_image> character_image; // bitmaps of all characters
53static hashmap<string,cg_image> images;
54
55
56
57/******************************************************************************
58 * cg_renderer
59 ******************************************************************************/
60
61void
62cg_set_color (CGContextRef cxt, color col) {
63 int r, g, b, a;
64 get_rgb_color (col, r, g, b, a);
65 CGContextSetRGBFillColor(cxt, r/255.0, g/255.0, b/255.0, a/255.0);
66 CGContextSetRGBStrokeColor(cxt, r/255.0, g/255.0, b/255.0, a/255.0);
67}
68
69cg_renderer_rep::cg_renderer_rep (int w2, int h2):
70 basic_renderer_rep(w2,h2), context(NULL)
71{
72}
73
74cg_renderer_rep::~cg_renderer_rep () {
75 if (context) end();
76}
77
78
79void
80cg_renderer_rep::begin (void * c) {
81 context = (CGContextRef)c;
82 CGContextRetain(context);
83 CGContextBeginPage(context, NULL);
84}
85
86void
87cg_renderer_rep::end () {
88 CGContextEndPage(context);
89 CGContextRelease(context);
90 context = NULL;
91}
92
93void
94cg_renderer_rep::next_page () {
95 CGContextEndPage(context);
96 CGContextBeginPage(context, NULL);
97}
98
99void
100cg_renderer_rep::set_color (color c) {
101 basic_renderer_rep::set_color(c);
102 cg_set_color(context,cur_fg);
103}
104
105void
106cg_renderer_rep::set_line_style (SI lw, int type, bool round) {
107 (void) type;
108
109 CGContextSetLineCap(context, round? kCGLineCapRound : kCGLineCapSquare);
110 CGContextSetLineJoin(context, kCGLineJoinRound);
111 CGContextSetLineWidth(context, lw <= pixel ? 1 : ((lw+thicken) / (1.0*pixel)));
112}
113
114void
115cg_renderer_rep::line (SI x1, SI y1, SI x2, SI y2) {
116 decode (x1, y1);
117 decode (x2, y2);
118 // y1--; y2--; // top-left origin to bottom-left origin conversion
119 CGContextSetShouldAntialias(context, true);
120 CGPoint points[2]= { CGPointMake(x1,y1), CGPointMake(x2,y2) };
121 CGContextStrokeLineSegments(context, points, 2);
122}
123
124void
125cg_renderer_rep::lines (array<SI> x, array<SI> y) {
126 int i, n= N(x);
127 if ((N(y) != n) || (n<1)) return;
128 STACK_NEW_ARRAY (pnt, CGPoint, n);
129 CGContextSetShouldAntialias(context, true);
130 for (i=0; i<n; i++) {
131 SI xx= x[i], yy= y[i];
132 decode (xx, yy);
133 pnt[i] = CGPointMake(xx,yy);
134 if (i>0) {
135 CGContextStrokeLineSegments(context, pnt + (i - 1), 2); // FIX: hack
136 }
137 }
138 STACK_DELETE_ARRAY (pnt);
139}
140
141void
142cg_renderer_rep::clear (SI x1, SI y1, SI x2, SI y2) {
143 x1= max (x1, cx1-ox); y1= max (y1, cy1-oy);
144 x2= min (x2, cx2-ox); y2= min (y2, cy2-oy);
145 // outer_round (x1, y1, x2, y2); might still be needed somewhere
146 decode (x1, y1);
147 decode (x2, y2);
148 if ((x1>=x2) || (y1<=y2)) return;
149 cg_set_color (context, cur_bg);
150 CGContextSetShouldAntialias(context, false);
151 CGContextFillRect(context, CGRectMake(x1, y2, x2-x1, y1-y2) );
152 cg_set_color (context, cur_fg);
153}
154
155void
156cg_renderer_rep::fill (SI x1, SI y1, SI x2, SI y2) {
157 if ((x2>x1) && ((x2-x1)<pixel)) {
158 SI d= pixel-(x2-x1);
159 x1 -= (d>>1);
160 x2 += ((d+1)>>1);
161 }
162 if ((y2>y1) && ((y2-y1)<pixel)) {
163 SI d= pixel-(y2-y1);
164 y1 -= (d>>1);
165 y2 += ((d+1)>>1);
166 }
167
168 x1= max (x1, cx1-ox); y1= max (y1, cy1-oy);
169 x2= min (x2, cx2-ox); y2= min (y2, cy2-oy);
170 // outer_round (x1, y1, x2, y2); might still be needed somewhere
171 if ((x1>=x2) || (y1>=y2)) return;
172
173 decode (x1, y1);
174 decode (x2, y2);
175
176 // cg_set_color (context, cur_fg);
177 CGContextSetShouldAntialias (context, false);
178 CGContextFillRect (context, CGRectMake(x1, y2, x2-x1, y1-y2) );
179}
180
181void
182cg_renderer_rep::arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
183 (void) alpha; (void) delta;
184 if ((x1>=x2) || (y1>=y2)) return;
185 decode (x1, y1);
186 decode (x2, y2);
187 //FIXME: XDrawArc (dpy, win, gc, x1, y2, x2-x1, y1-y2, alpha, delta);
188}
189
190void
191cg_renderer_rep::fill_arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
192 (void) alpha; (void) delta;
193 if ((x1>=x2) || (y1>=y2)) return;
194 decode (x1, y1);
195 decode (x2, y2);
196 //FIXME: XFillArc (dpy, win, gc, x1, y2, x2-x1, y1-y2, alpha, delta);
197}
198
199void
200cg_renderer_rep::polygon (array<SI> x, array<SI> y, bool convex) {
201 int i, n= N(x);
202 if ((N(y) != n) || (n<1)) return;
203
204 CGContextBeginPath(context);
205 for (i=0; i<n; i++) {
206 SI xx= x[i], yy= y[i];
207 decode (xx, yy);
208 if (i==0) CGContextMoveToPoint (context, xx, yy);
209 else CGContextAddLineToPoint(context, xx ,yy);
210 }
211 CGContextClosePath (context);
212// cg_set_color (context, cur_fg);
213 CGContextSetShouldAntialias (context, true);
214 if (convex) CGContextEOFillPath (context);
215 else CGContextFillPath (context);
216}
217
218
219/******************************************************************************
220* Image rendering
221******************************************************************************/
222struct cg_cache_image_rep: cache_image_element_rep {
223 cg_cache_image_rep (int w2, int h2, time_t time2, CGImageRef ptr2) :
224 cache_image_element_rep(w2,h2,time2,ptr2) { CGImageRetain((CGImageRef)ptr); };
225 virtual ~cg_cache_image_rep() { CGImageRelease((CGImageRef)ptr); };
226};
227
228void
229cg_renderer_rep::image (url u, SI w, SI h, SI x, SI y,
230 double cx1, double cy1, double cx2, double cy2,
231 int alpha)
232{
233 // Given an image of original size (W, H),
234 // we display the part (cx1 * W, xy1 * H, cx2 * W, cy2 * H)
235 // at position (x, y) in a rectangle of size (w, h)
236
237 // if (DEBUG_EVENTS) cout << "cg_renderer_rep::image " << as_string(u) << LF;
238 (void) alpha;
239
240 w= w/pixel; h= h/pixel;
241 decode (x, y);
242
243 //painter.setRenderHints (0);
244 //painter.drawRect (QRect (x, y-h, w, h));
245
246 CGImageRef pm = NULL;
247 tree lookup= tuple (u->t);
248 lookup << as_string (w ) << as_string (h )
249 << as_string (cx1) << as_string (cy1)
250 << as_string (cx2) << as_string (cy2) << "cg-image" ;
251 cache_image_element ci = get_image_cache(lookup);
252 if (!is_nil(ci)) {
253 pm = static_cast<CGImageRef> (ci->ptr);
254 } else {
255 if (suffix (u) == "png") {
256 // rendering
257 string suu = as_string (u);
258 char * buf = as_charp(suu);
259 // cout << suu << LF;
260 CFURLRef uu = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)buf, N(suu), false);
261 tm_delete (buf);
262 CGImageSourceRef source = CGImageSourceCreateWithURL ( uu, NULL );
263 pm = CGImageSourceCreateImageAtIndex(source, 0, NULL);
264 CFRelease(source);
265 CFRelease(uu);
266 } else if (suffix (u) == "ps" ||
267 suffix (u) == "eps" ||
268 suffix (u) == "pdf") {
269 url temp= url_temp (".png");
270// system ("convert", u, temp);
271 mac_image_to_png (u, temp);
272 string suu = as_string (temp);
273 char * buf = as_charp(suu);
274 //cout << suu << LF;
275 CFURLRef uu = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)buf, N(suu), false);
276 tm_delete (buf);
277 CGImageSourceRef source = CGImageSourceCreateWithURL ( uu, NULL );
278 pm = CGImageSourceCreateImageAtIndex(source, 0, NULL);
279 CFRelease(source);
280 CFRelease(uu);
281 remove (temp);
282 }
283
284 if (pm == NULL ) {
285 cout << "TeXmacs] warning: cannot render " << as_string (u) << "\n";
286 return;
287 }
288 // caching
289 ci = tm_new <cg_cache_image_rep> (w,h, texmacs_time(), pm);
290 set_image_cache(lookup, ci);
291 (ci->nr)++;
292 }
293
294 CGContextSetShouldAntialias(context, false);
295 CGContextSaveGState(context);
296 CGContextTranslateCTM(context, x,y);
297 CGContextScaleCTM(context,1.0,-1.0);
298 CGContextDrawImage(context, CGRectMake(0, 0, w, h), pm);
299 CGContextRestoreGState(context);
300}
301
302
303
304void
305cg_renderer_rep::draw_clipped (CGImageRef im, int w, int h, SI x, SI y) {
306 decode (x , y );
307 y--; // top-left origin to bottom-left origin conversion
308 // clear(x1,y1,x2,y2);
309 CGContextSetShouldAntialias(context, true);
310// CGContextSetBlendMode(context,kCGBlendModeSourceAtop);
311 CGContextDrawImage(context, CGRectMake(x,y,w,h), im);
312}
313
314
315
316
317static hashmap<string,pointer> native_fonts;
318static hashset<string> native_loaded;
319
320int
321posixStringToFSSpec(FSSpec *fss, CFStringRef posixPath, bool isDirectory) {
322 FSRef fsRef;
323 FSSpec fileSpec;
324 // create a URL from the posix path:
325 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,posixPath,kCFURLPOSIXPathStyle,isDirectory);
326 // check to be sure the URL was created properly:
327 if (url == 0) {
328 //fprintf(stderr,"Can't get URL");
329 return(1);
330 }
331 // use the CF function to extract an FSRef from the URL:
332 if (CFURLGetFSRef(url, &fsRef) == 0){
333 //fprintf(stderr,"Can't get FSRef.\n");
334 CFRelease(url);
335 return(1);
336 }
337 // use Carbon call to get the FSSpec from the FSRef
338 if (FSGetCatalogInfo (&fsRef, kFSCatInfoNone, 0, 0, &fileSpec, 0) != noErr) {
339 //fprintf(stderr,"Can't get FSSpec.\n");
340 CFRelease(url);
341 return(1);
342 }
343 // We have a valid FSSpec! Clean up and return it:
344 CFRelease(url);
345 *fss = fileSpec;
346 return 0;
347}
348
349
350
351bool
352cg_renderer_rep::native_draw (int ch, font_glyphs fn, SI x, SI y) {
353 string name= fn->res_name;
354 unsigned char c= ch;
355 if (ch >= 256) {
356 name= name * "-" * as_string (ch / 256);
357 c= (unsigned char) (ch & 255);
358 }
359
360 // cout << name << LF;
361 int size;
362 {
363 // find size (weird)
364 int pos1 = search_forwards (".", name);
365 int pos2= search_backwards (":", name);
366 string sz = name(pos2+1,pos1);
367 size = as_int(sz);
368 }
369 CGFontRef f = (CGFontRef)native_fonts(name);
370
371 if ((f == NULL)&&(! native_loaded->contains(name))) {
372 native_loaded->insert(name);
373 string ttf;
374 int pos = search_forwards (".", name);
375 string root = (pos==-1? name: name (0, pos));
376 if ((pos!=-1) && ends (name, "tt")) {
377 int pos2= search_backwards (":", name);
378 root= name (0, pos2);
379 url u= tt_font_find (root);
380 if (suffix (u) == "pfb") {
381// cout << u << LF;
382 url v= url_temp (".otf");
383 string vs = concretize(v);
384 system ("/Users/mgubi/t/t1wrap/T1Wrap " * concretize(u) * " > " * vs);
385 FSSpec fss;
386 ATSFontRef atsFont;
387 ATSFontContainerRef container;
388 char *p = as_charp(vs);
389 CFStringRef font_filename = CFStringCreateWithCString(NULL,p,kCFStringEncodingASCII);
390
391 if (posixStringToFSSpec(&fss,font_filename,false)) {
392 cout << "Cannot load font" << vs << LF;
393 } else {
394 int status = ATSFontActivateFromFileSpecification(&fss,kATSFontContextLocal,kATSFontFormatUnspecified,NULL,NULL,&container);
395 cout << "Font " << vs << " loaded" << LF;
396 ItemCount count;
397 status = ATSFontFindFromContainer(container, 0, 1, &atsFont, &count);
398
399 f = CGFontCreateWithPlatformFont((void*)&atsFont);
400 native_fonts(name) = f;
401 }
402 tm_delete (p);
403 CFRelease(font_filename);
404 remove (v);
405 }
406 }
407 } // end caching
408
409 if (f) {
410 decode (x , y );
411 y--; // top-left origin to bottom-left origin conversion
412 CGContextRef cgc = context;
413 CGContextSetFont(cgc,f);
414 CGContextSetFontSize(cgc,size);
415 CGAffineTransform kHorizontalMatrix = { PIXEL*600.0/(pixel*72.0), 0.0, 0.0, -PIXEL*600.0/(pixel*72.0), 0.0, 0.0 };
416 CGContextSetTextMatrix(cgc, kHorizontalMatrix);
417 CGContextSetTextDrawingMode(cgc, kCGTextFill);
418 CGContextSetShouldAntialias(cgc,true);
419 CGContextSetShouldSmoothFonts(cgc,true);
420 // CGContextSetBlendMode(context,kCGBlendModeSourceAtop);
421 // cg_set_color (context, cur_fg);
422 CGGlyph buf[1] = {c};
423 CGContextShowGlyphsAtPoint(cgc,x,y,(CGGlyph*)buf,1);
424 }
425 return true;
426}
427
428
429CGContextRef
430MyCreateBitmapContext (int pixelsWide, int pixelsHigh) {
431 int bitmapBytesPerRow = (pixelsWide * 4);
432 int bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
433 CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
434 void *bitmapData = malloc( bitmapByteCount );
435 if (bitmapData == NULL) {
436 //fprintf (stderr, "Memory not allocated!");
437 return NULL;
438 }
439 CGContextRef context = CGBitmapContextCreate (bitmapData, pixelsWide, pixelsHigh, 8,
440 bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
441 if (context == NULL) {
442 free (bitmapData);
443 // fprintf (stderr, "Context not created!");
444 return NULL;
445 }
446 CGColorSpaceRelease (colorSpace);
447 return context;
448}
449
450
451void
452cg_renderer_rep::draw (int c, font_glyphs fng, SI x, SI y) {
453 // get the pixmap
454 basic_character xc (c, fng, sfactor, 0, 0);
455 cg_image mi = character_image [xc];
456 if (is_nil(mi)) {
457 SI xo, yo;
458 glyph pre_gl= fng->get (c); if (is_nil (pre_gl)) return;
459 glyph gl= shrink (pre_gl, sfactor, sfactor, xo, yo);
460 int i, j, w= gl->width, h= gl->height;
461 CGImageRef im = NULL;
462 {
463 CGContextRef ic = MyCreateBitmapContext(w,h);
464 int nr_cols= sfactor*sfactor;
465 if (nr_cols >= 64) nr_cols= 64;
466 //CGContextSetShouldAntialias(ic,true);
467 CGContextSetBlendMode(ic,kCGBlendModeCopy);
468 //CGContextSetRGBFillColor(ic,1.0,1.0,1.0,0.0);
469 //CGContextFillRect(ic,CGRectMake(0,0,w,h));
470
471 for (j=0; j<h; j++)
472 for (i=0; i<w; i++) {
473 int col = gl->get_x (i, j);
474 CGContextSetRGBFillColor(ic, 0.0,0.0,0.0, ((255*col)/(nr_cols+1))/255.0);
475 CGContextFillRect(ic,CGRectMake(i,j,1,1));
476 }
477 im = CGBitmapContextCreateImage (ic);
478 CGContextRelease (ic);
479 }
480 cg_image mi2 (im, xo, yo, w, h);
481 mi = mi2;
482 CGImageRelease(im); // cg_image retains im
483 character_image (xc)= mi;
484 }
485
486 // draw the character
487 {
488 (void) w; (void) h;
489 int x1= x- mi->xo*sfactor;
490 int y1= y+ mi->yo*sfactor;
491 decode (x1, y1);
492 y1--; // top-left origin to bottom-left origin conversion
493 CGRect r = CGRectMake(x1,y1,mi->w,mi->h);
494 CGContextSetShouldAntialias (context, true);
495 CGContextSaveGState (context);
496 // cg_set_color (context, cur_fg);
497 CGContextClipToMask (context, r, mi->img);
498 CGContextFillRect (context, r);
499 CGContextRestoreGState (context);
500 }
501}
502
503/******************************************************************************
504* Setting up and displaying xpm pixmaps
505******************************************************************************/
506
507
508
509static CGImageRef xpm_init(url file_name)
510{
511 tree t= xpm_load (file_name);
512
513 // get main info
514 int ok, i=0, j, k, w, h, c, b, x, y;
515 string s= as_string (t[0]);
516 skip_spaces (s, i);
517 ok= read_int (s, i, w);
518 skip_spaces (s, i);
519 ok= read_int (s, i, h) && ok;
520 skip_spaces (s, i);
521 ok= read_int (s, i, c) && ok;
522 skip_spaces (s, i);
523 ok= read_int (s, i, b) && ok;
524 if ((!ok) || (N(t)<(c+1)) || (c<=0)) {
525 cerr << "File name= " << file_name << "\n";
526 FAILED ("invalid xpm");
527 }
528
529 // setup colors
530 string first_name;
531 hashmap<string,color> pmcs;
532 for (k=0; k<c; k++) {
533 string s = as_string (t[k+1]);
534 string name= "";
535 string def = "none";
536 if (N(s)<b) i=N(s);
537 else { name= s(0,b); i=b; }
538 if (k==0) first_name= name;
539
540 skip_spaces (s, i);
541 if ((i<N(s)) && (s[i]=='s')) {
542 i++;
543 skip_spaces (s, i);
544 while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
545 skip_spaces (s, i);
546 }
547 if ((i<N(s)) && (s[i]=='c')) {
548 i++;
549 skip_spaces (s, i);
550 j=i;
551 while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
552 def= locase_all (s (j, i));
553 }
554
555 pmcs(name)= xpm_to_color(def);
556 }
557 CGContextRef ic = MyCreateBitmapContext(w,h);
558 CGContextSetBlendMode(ic,kCGBlendModeCopy);
559 // setup pixmap
560 for (y=0; y<h; y++) {
561 if (N(t)< (y+c+1)) s= "";
562 else s= as_string (t[y+c+1]);
563 for (x=0; x<w; x++) {
564 string name;
565 if (N(s)<(b*(x+1))) name= first_name;
566 else name= s (b*x, b*(x+1));
567 color col = pmcs[(pmcs->contains (name) ? name : first_name)];
568 cg_set_color (ic, col);
569 CGContextFillRect (ic,CGRectMake(x,y,1,1));
570 }
571 }
572 CGImageRef im = CGBitmapContextCreateImage (ic);
573 CGContextRelease (ic);
574 return im;
575}
576
577
578
579extern int char_clip;
580
581CGImageRef
582cg_renderer_rep::xpm_image (url file_name) {
583 CGImageRef pxm= NULL;
584 cg_image mi= images [as_string (file_name)];
585 if (is_nil (mi)) {
586 pxm = xpm_init(file_name);
587 cg_image mi2 (pxm, 0, 0, CGImageGetWidth (pxm), CGImageGetHeight (pxm));
588 mi= mi2;
589 images (as_string (file_name))= mi2;
590 CGImageRelease(pxm);
591 } else pxm= mi->img;
592 return pxm;
593}
594
595void
596cg_renderer_rep::xpm (url file_name, SI x, SI y) {
597 y -= pixel; // counter balance shift in draw_clipped
598 CGImageRef image = xpm_image (file_name);
599 ASSERT (sfactor == 1, "shrinking factor should be 1");
600 int w = CGImageGetWidth(image);
601 int h = CGImageGetHeight(image);
602 int old_clip= char_clip;
603 char_clip = true;
604 draw_clipped (image, w, h, x, y);
605 char_clip = old_clip;
606}
607
608/******************************************************************************
609 * main coregraphics renderer
610 ******************************************************************************/
611
612static cg_renderer_rep* the_renderer= NULL;
613
614cg_renderer_rep*
615the_cg_renderer () {
616 if (!the_renderer) the_renderer= tm_new <cg_renderer_rep> ();
617 return the_renderer;
618}