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
******************************************************************************/
13
#include "cg_renderer.hpp"
14
#include "analyze.hpp"
15
#include "image_files.hpp"
17
#include "iterator.hpp"
18
#include "gui.hpp" // for INTERRUPT_EVENT, INTERRUPTED_EVENT
19
#include "font.hpp" // for the definition of font
21
#include "Freetype/tt_file.hpp" // tt_font_find
23
/******************************************************************************
25
******************************************************************************/
27
struct cg_image_rep: concrete_struct {
31
cg_image_rep (CGImageRef img2, SI xo2, SI yo2, int w2, int h2) :
32
img (img2), xo (xo2), yo (yo2), w (w2), h (h2) { CGImageRetain(img); };
33
~cg_image_rep() { CGImageRelease(img); };
34
friend class cg_image;
38
CONCRETE_NULL(cg_image);
39
cg_image (CGImageRef img2, SI xo2, SI yo2, int w2, int h2):
40
rep (tm_new <cg_image_rep> (img2, xo2, yo2, w2, h2)) {}
43
CONCRETE_NULL_CODE(cg_image);
45
/******************************************************************************
46
* Global support variables for all cg_renderers
47
******************************************************************************/
50
static hashmap<basic_character,cg_image> character_image; // bitmaps of all characters
51
static hashmap<string,cg_image> images;
55
/******************************************************************************
57
******************************************************************************/
60
cg_set_color (CGContextRef cxt, color col) {
62
get_rgb_color(col,r,g,b);
63
CGContextSetRGBFillColor(cxt, r/255.0, g/255.0, b/255.0, 1.0);
64
CGContextSetRGBStrokeColor(cxt, r/255.0, g/255.0, b/255.0, 1.0);
67
cg_renderer_rep::cg_renderer_rep (int w2, int h2):
68
basic_renderer_rep(w2,h2), context(NULL)
72
cg_renderer_rep::~cg_renderer_rep () {
78
cg_renderer_rep::begin (void * c) {
79
context = (CGContextRef)c;
80
CGContextRetain(context);
81
CGContextBeginPage(context, NULL);
85
cg_renderer_rep::end () {
86
CGContextEndPage(context);
87
CGContextRelease(context);
92
cg_renderer_rep::next_page () {
93
CGContextEndPage(context);
94
CGContextBeginPage(context, NULL);
98
cg_renderer_rep::set_color (color c) {
99
basic_renderer_rep::set_color(c);
100
cg_set_color(context,cur_fg);
104
cg_renderer_rep::set_line_style (SI lw, int type, bool round) {
107
CGContextSetLineCap(context, round? kCGLineCapRound : kCGLineCapSquare);
108
CGContextSetLineJoin(context, kCGLineJoinRound);
109
CGContextSetLineWidth(context, lw <= pixel ? 1 : ((lw+thicken) / (1.0*pixel)));
113
cg_renderer_rep::line (SI x1, SI y1, SI x2, SI y2) {
116
// y1--; y2--; // top-left origin to bottom-left origin conversion
117
CGContextSetShouldAntialias(context, true);
118
CGPoint points[2]= { CGPointMake(x1,y1), CGPointMake(x2,y2) };
119
CGContextStrokeLineSegments(context, points, 2);
123
cg_renderer_rep::lines (array<SI> x, array<SI> y) {
125
if ((N(y) != n) || (n<1)) return;
126
STACK_NEW_ARRAY (pnt, CGPoint, n);
127
CGContextSetShouldAntialias(context, true);
128
for (i=0; i<n; i++) {
129
SI xx= x[i], yy= y[i];
131
pnt[i] = CGPointMake(xx,yy);
133
CGContextStrokeLineSegments(context, pnt + (i - 1), 2); // FIX: hack
136
STACK_DELETE_ARRAY (pnt);
140
cg_renderer_rep::clear (SI x1, SI y1, SI x2, SI y2) {
141
x1= max (x1, cx1-ox); y1= max (y1, cy1-oy);
142
x2= min (x2, cx2-ox); y2= min (y2, cy2-oy);
143
// outer_round (x1, y1, x2, y2); might still be needed somewhere
146
if ((x1>=x2) || (y1<=y2)) return;
147
cg_set_color (context, cur_bg);
148
CGContextSetShouldAntialias(context, false);
149
CGContextFillRect(context, CGRectMake(x1, y2, x2-x1, y1-y2) );
150
cg_set_color (context, cur_fg);
154
cg_renderer_rep::fill (SI x1, SI y1, SI x2, SI y2) {
155
if ((x2>x1) && ((x2-x1)<pixel)) {
160
if ((y2>y1) && ((y2-y1)<pixel)) {
166
x1= max (x1, cx1-ox); y1= max (y1, cy1-oy);
167
x2= min (x2, cx2-ox); y2= min (y2, cy2-oy);
168
// outer_round (x1, y1, x2, y2); might still be needed somewhere
169
if ((x1>=x2) || (y1>=y2)) return;
174
// cg_set_color (context, cur_fg);
175
CGContextSetShouldAntialias (context, false);
176
CGContextFillRect (context, CGRectMake(x1, y2, x2-x1, y1-y2) );
180
cg_renderer_rep::arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
181
(void) alpha; (void) delta;
182
if ((x1>=x2) || (y1>=y2)) return;
185
//FIXME: XDrawArc (dpy, win, gc, x1, y2, x2-x1, y1-y2, alpha, delta);
189
cg_renderer_rep::fill_arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
190
(void) alpha; (void) delta;
191
if ((x1>=x2) || (y1>=y2)) return;
194
//FIXME: XFillArc (dpy, win, gc, x1, y2, x2-x1, y1-y2, alpha, delta);
198
cg_renderer_rep::polygon (array<SI> x, array<SI> y, bool convex) {
200
if ((N(y) != n) || (n<1)) return;
202
CGContextBeginPath(context);
203
for (i=0; i<n; i++) {
204
SI xx= x[i], yy= y[i];
206
if (i==0) CGContextMoveToPoint (context, xx, yy);
207
else CGContextAddLineToPoint(context, xx ,yy);
209
CGContextClosePath (context);
210
// cg_set_color (context, cur_fg);
211
CGContextSetShouldAntialias (context, true);
212
if (convex) CGContextEOFillPath (context);
213
else CGContextFillPath (context);
217
/******************************************************************************
219
******************************************************************************/
220
struct cg_cache_image_rep: cache_image_element_rep {
221
cg_cache_image_rep (int w2, int h2, int time2, CGImageRef ptr2) :
222
cache_image_element_rep(w2,h2,time2,ptr2) { CGImageRetain((CGImageRef)ptr); };
223
virtual ~cg_cache_image_rep() { CGImageRelease((CGImageRef)ptr); };
227
cg_renderer_rep::image (url u, SI w, SI h, SI x, SI y,
228
double cx1, double cy1, double cx2, double cy2)
230
// Given an image of original size (W, H),
231
// we display the part (cx1 * W, xy1 * H, cx2 * W, cy2 * H)
232
// at position (x, y) in a rectangle of size (w, h)
234
// if (DEBUG_EVENTS) cout << "cg_renderer_rep::image " << as_string(u) << LF;
236
w= w/pixel; h= h/pixel;
239
//painter.setRenderHints (0);
240
//painter.drawRect (QRect (x, y-h, w, h));
242
CGImageRef pm = NULL;
243
tree lookup= tuple (u->t);
244
lookup << as_string (w ) << as_string (h )
245
<< as_string (cx1) << as_string (cy1)
246
<< as_string (cx2) << as_string (cy2) << "cg-image" ;
247
cache_image_element ci = get_image_cache(lookup);
249
pm = static_cast<CGImageRef> (ci->ptr);
251
if (suffix (u) == "png") {
253
string suu = as_string (u);
254
char * buf = as_charp(suu);
255
// cout << suu << LF;
256
CFURLRef uu = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)buf, N(suu), false);
258
CGImageSourceRef source = CGImageSourceCreateWithURL ( uu, NULL );
259
pm = CGImageSourceCreateImageAtIndex(source, 0, NULL);
262
} else if (suffix (u) == "ps" ||
263
suffix (u) == "eps" ||
264
suffix (u) == "pdf") {
265
url temp= url_temp (".png");
266
system ("convert", u, temp);
267
string suu = as_string (temp);
268
char * buf = as_charp(suu);
270
CFURLRef uu = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)buf, N(suu), false);
272
CGImageSourceRef source = CGImageSourceCreateWithURL ( uu, NULL );
273
pm = CGImageSourceCreateImageAtIndex(source, 0, NULL);
280
cout << "TeXmacs] warning: cannot render " << as_string (u) << "\n";
284
ci = tm_new <cg_cache_image_rep> (w,h, texmacs_time(), pm);
285
set_image_cache(lookup, ci);
289
CGContextSetShouldAntialias(context, false);
290
CGContextSaveGState(context);
291
CGContextTranslateCTM(context, x,y);
292
CGContextScaleCTM(context,1.0,-1.0);
293
CGContextDrawImage(context, CGRectMake(0, 0, w, h), pm);
294
CGContextRestoreGState(context);
300
cg_renderer_rep::draw_clipped (CGImageRef im, int w, int h, SI x, SI y) {
302
y--; // top-left origin to bottom-left origin conversion
303
// clear(x1,y1,x2,y2);
304
CGContextSetShouldAntialias(context, true);
305
// CGContextSetBlendMode(context,kCGBlendModeSourceAtop);
306
CGContextDrawImage(context, CGRectMake(x,y,w,h), im);
312
static hashmap<string,pointer> native_fonts;
313
static hashset<string> native_loaded;
316
posixStringToFSSpec(FSSpec *fss, CFStringRef posixPath, bool isDirectory) {
319
// create a URL from the posix path:
320
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,posixPath,kCFURLPOSIXPathStyle,isDirectory);
321
// check to be sure the URL was created properly:
323
//fprintf(stderr,"Can't get URL");
326
// use the CF function to extract an FSRef from the URL:
327
if (CFURLGetFSRef(url, &fsRef) == 0){
328
//fprintf(stderr,"Can't get FSRef.\n");
332
// use Carbon call to get the FSSpec from the FSRef
333
if (FSGetCatalogInfo (&fsRef, kFSCatInfoNone, 0, 0, &fileSpec, 0) != noErr) {
334
//fprintf(stderr,"Can't get FSSpec.\n");
338
// We have a valid FSSpec! Clean up and return it:
347
cg_renderer_rep::native_draw (int ch, font_glyphs fn, SI x, SI y) {
348
string name= fn->res_name;
351
name= name * "-" * as_string (ch / 256);
352
c= (unsigned char) (ch & 255);
355
// cout << name << LF;
359
int pos1 = search_forwards (".", name);
360
int pos2= search_backwards (":", name);
361
string sz = name(pos2+1,pos1);
364
CGFontRef f = (CGFontRef)native_fonts(name);
366
if ((f == NULL)&&(! native_loaded->contains(name))) {
367
native_loaded->insert(name);
369
int pos = search_forwards (".", name);
370
string root = (pos==-1? name: name (0, pos));
371
if ((pos!=-1) && ends (name, "tt")) {
372
int pos2= search_backwards (":", name);
373
root= name (0, pos2);
374
url u= tt_font_find (root);
375
if (suffix (u) == "pfb") {
377
url v= url_temp (".otf");
378
string vs = concretize(v);
379
system ("/Users/mgubi/t/t1wrap/T1Wrap " * concretize(u) * " > " * vs);
382
ATSFontContainerRef container;
383
char *p = as_charp(vs);
384
CFStringRef font_filename = CFStringCreateWithCString(NULL,p,kCFStringEncodingASCII);
386
if (posixStringToFSSpec(&fss,font_filename,false)) {
387
cout << "Cannot load font" << vs << LF;
389
int status = ATSFontActivateFromFileSpecification(&fss,kATSFontContextLocal,kATSFontFormatUnspecified,NULL,NULL,&container);
390
cout << "Font " << vs << " loaded" << LF;
392
status = ATSFontFindFromContainer(container, 0, 1, &atsFont, &count);
394
f = CGFontCreateWithPlatformFont((void*)&atsFont);
395
native_fonts(name) = f;
398
CFRelease(font_filename);
406
y--; // top-left origin to bottom-left origin conversion
407
CGContextRef cgc = context;
408
CGContextSetFont(cgc,f);
409
CGContextSetFontSize(cgc,size);
410
CGAffineTransform kHorizontalMatrix = { PIXEL*600.0/(pixel*72.0), 0.0, 0.0, -PIXEL*600.0/(pixel*72.0), 0.0, 0.0 };
411
CGContextSetTextMatrix(cgc, kHorizontalMatrix);
412
CGContextSetTextDrawingMode(cgc, kCGTextFill);
413
CGContextSetShouldAntialias(cgc,true);
414
CGContextSetShouldSmoothFonts(cgc,true);
415
// CGContextSetBlendMode(context,kCGBlendModeSourceAtop);
416
// cg_set_color (context, cur_fg);
417
CGGlyph buf[1] = {c};
418
CGContextShowGlyphsAtPoint(cgc,x,y,(CGGlyph*)buf,1);
425
MyCreateBitmapContext (int pixelsWide, int pixelsHigh) {
426
int bitmapBytesPerRow = (pixelsWide * 4);
427
int bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
428
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
429
void *bitmapData = malloc( bitmapByteCount );
430
if (bitmapData == NULL) {
431
//fprintf (stderr, "Memory not allocated!");
434
CGContextRef context = CGBitmapContextCreate (bitmapData, pixelsWide, pixelsHigh, 8,
435
bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
436
if (context == NULL) {
438
// fprintf (stderr, "Context not created!");
441
CGColorSpaceRelease (colorSpace);
447
cg_renderer_rep::draw (int c, font_glyphs fng, SI x, SI y) {
449
basic_character xc (c, fng, sfactor, 0, 0);
450
cg_image mi = character_image [xc];
453
glyph pre_gl= fng->get (c); if (is_nil (pre_gl)) return;
454
glyph gl= shrink (pre_gl, sfactor, sfactor, xo, yo);
455
int i, j, w= gl->width, h= gl->height;
456
CGImageRef im = NULL;
458
CGContextRef ic = MyCreateBitmapContext(w,h);
459
int nr_cols= sfactor*sfactor;
460
if (nr_cols >= 64) nr_cols= 64;
461
//CGContextSetShouldAntialias(ic,true);
462
CGContextSetBlendMode(ic,kCGBlendModeCopy);
463
//CGContextSetRGBFillColor(ic,1.0,1.0,1.0,0.0);
464
//CGContextFillRect(ic,CGRectMake(0,0,w,h));
467
for (i=0; i<w; i++) {
468
int col = gl->get_x (i, j);
469
CGContextSetRGBFillColor(ic, 0.0,0.0,0.0, ((255*col)/(nr_cols+1))/255.0);
470
CGContextFillRect(ic,CGRectMake(i,j,1,1));
472
im = CGBitmapContextCreateImage (ic);
473
CGContextRelease (ic);
475
cg_image mi2 (im, xo, yo, w, h);
477
CGImageRelease(im); // cg_image retains im
478
character_image (xc)= mi;
481
// draw the character
484
int x1= x- mi->xo*sfactor;
485
int y1= y+ mi->yo*sfactor;
487
y1--; // top-left origin to bottom-left origin conversion
488
CGRect r = CGRectMake(x1,y1,mi->w,mi->h);
489
CGContextSetShouldAntialias (context, true);
490
CGContextSaveGState (context);
491
// cg_set_color (context, cur_fg);
492
CGContextClipToMask (context, r, mi->img);
493
CGContextFillRect (context, r);
494
CGContextRestoreGState (context);
498
/******************************************************************************
499
* Setting up and displaying xpm pixmaps
500
******************************************************************************/
504
static CGImageRef xpm_init(url file_name)
506
tree t= xpm_load (file_name);
509
int ok, i=0, j, k, w, h, c, b, x, y;
510
string s= as_string (t[0]);
512
ok= read_int (s, i, w);
514
ok= read_int (s, i, h) && ok;
516
ok= read_int (s, i, c) && ok;
518
ok= read_int (s, i, b) && ok;
519
if ((!ok) || (N(t)<(c+1)) || (c<=0)) {
520
cerr << "File name= " << file_name << "\n";
521
FAILED ("invalid xpm");
526
hashmap<string,color> pmcs;
527
for (k=0; k<c; k++) {
528
string s = as_string (t[k+1]);
532
else { name= s(0,b); i=b; }
533
if (k==0) first_name= name;
536
if ((i<N(s)) && (s[i]=='s')) {
539
while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
542
if ((i<N(s)) && (s[i]=='c')) {
546
while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
547
def= locase_all (s (j, i));
550
pmcs(name)= xpm_to_color(def);
552
CGContextRef ic = MyCreateBitmapContext(w,h);
553
CGContextSetBlendMode(ic,kCGBlendModeCopy);
555
for (y=0; y<h; y++) {
556
if (N(t)< (y+c+1)) s= "";
557
else s= as_string (t[y+c+1]);
558
for (x=0; x<w; x++) {
560
if (N(s)<(b*(x+1))) name= first_name;
561
else name= s (b*x, b*(x+1));
562
color col = pmcs[(pmcs->contains (name) ? name : first_name)];
563
cg_set_color (ic, col);
564
CGContextFillRect (ic,CGRectMake(x,y,1,1));
567
CGImageRef im = CGBitmapContextCreateImage (ic);
568
CGContextRelease (ic);
574
extern int char_clip;
577
cg_renderer_rep::xpm_image (url file_name) {
578
CGImageRef pxm= NULL;
579
cg_image mi= images [as_string (file_name)];
581
pxm = xpm_init(file_name);
582
cg_image mi2 (pxm, 0, 0, CGImageGetWidth (pxm), CGImageGetHeight (pxm));
584
images (as_string (file_name))= mi2;
591
cg_renderer_rep::xpm (url file_name, SI x, SI y) {
592
y -= pixel; // counter balance shift in draw_clipped
593
CGImageRef image = xpm_image (file_name);
594
ASSERT (sfactor == 1, "shrinking factor should be 1");
595
int w = CGImageGetWidth(image);
596
int h = CGImageGetHeight(image);
597
int old_clip= char_clip;
599
draw_clipped (image, w, h, x, y);
600
char_clip = old_clip;
603
/******************************************************************************
604
* main coregraphics renderer
605
******************************************************************************/
607
static cg_renderer_rep* the_renderer= NULL;
611
if (!the_renderer) the_renderer= tm_new <cg_renderer_rep> ();