1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "edgelist2.h"
31 #include "fffreetype.h"
32 #include "fontforgeui.h"
33 #include "gwidget.h"
34 #include "ustring.h"
35 
36 #include <math.h>
37 
38 /******************************************************************************/
39 /* ***************************** Debugger Stuff ***************************** */
40 /******************************************************************************/
41 
42 #if FREETYPE_HAS_DEBUGGER
43 
44 #include <pthread.h>
45 #include <tterrors.h>
46 
47 typedef struct bpdata {
48     int range;	/* tt_coderange_glyph, tt_coderange_font, tt_coderange_cvt */
49     int ip;
50 } BpData;
51 
52 struct debugger_context {
53     FT_Library context;
54     FTC *ftc;
55     /* I use a thread because freetype doesn't return, it just has a callback */
56     /*  on each instruction. In actuallity only one thread should be executable*/
57     /*  at a time (either main, or child) */
58     pthread_t thread;
59     pthread_mutex_t parent_mutex, child_mutex;
60     pthread_cond_t parent_cond, child_cond;
61     unsigned int terminate: 1;		/* The thread has been started simply to clean itself up and die */
62     unsigned int has_mutexes: 1;
63     unsigned int has_thread: 1;
64     unsigned int has_finished: 1;
65     unsigned int debug_fpgm: 1;
66     unsigned int multi_step: 1;
67     unsigned int found_wp: 1;
68     unsigned int found_wps: 1;
69     unsigned int found_wps_uninit: 1;
70     unsigned int found_wpc: 1;
71     unsigned int initted_pts: 1;
72     unsigned int is_bitmap: 1;
73     int wp_ptindex, wp_cvtindex, wp_storeindex;
74     real ptsizey, ptsizex;
75     int dpi;
76     TT_ExecContext exc;
77     SplineChar *sc;
78     int layer;
79     BpData temp;
80     BpData breaks[32];
81     int bcnt;
82     FT_Vector *oldpts;
83     FT_Long *oldstore;
84     uint8 *storetouched;
85     int storeSize;
86     FT_Long *oldcvt;
87     FT_Long oldsval, oldcval;
88     int n_points;
89     uint8 *watch;		/* exc->pts.n_points */
90     uint8 *watchstorage;	/* exc->storeSize, exc->storage[i] */
91     uint8 *watchcvt;		/* exc->cvtSize, exc->cvt[i] */
92     int uninit_index;
93 };
94 
AtWp(struct debugger_context * dc,TT_ExecContext exc)95 static int AtWp(struct debugger_context *dc, TT_ExecContext exc ) {
96     int i, hit=false, h;
97 
98     dc->found_wp = false;
99     if ( dc->watch!=NULL && dc->oldpts!=NULL ) {
100 	for ( i=0; i<exc->pts.n_points; ++i ) {
101 	    if ( dc->oldpts[i].x!=exc->pts.cur[i].x || dc->oldpts[i].y!=exc->pts.cur[i].y ) {
102 		dc->oldpts[i] = exc->pts.cur[i];
103 		if ( dc->watch[i] ) {
104 		    hit = true;
105 		    dc->wp_ptindex = i;
106 		}
107 	    }
108 	}
109 	dc->found_wp = hit;
110     }
111     if ( dc->found_wps_uninit )
112 	hit = true;
113     dc->found_wps = false;
114     if ( dc->watchstorage!=NULL && dc->storetouched!=NULL ) {
115 	h = false;
116 	for ( i=0; i<exc->storeSize; ++i ) {
117 	    if ( dc->storetouched[i]&2 ) {
118 		if ( dc->watchstorage[i] ) {
119 		    h = true;
120 		    dc->wp_storeindex = i;
121 		    dc->oldsval = dc->oldstore[i];
122 		}
123 		dc->storetouched[i]&=~2;
124 		dc->oldstore[i] = exc->storage[i];
125 	    }
126 	}
127 	dc->found_wps = h;
128 	hit |= h;
129     }
130     dc->found_wpc = false;
131     if ( dc->watchcvt!=NULL && dc->oldcvt!=NULL ) {
132 	h = false;
133 	for ( i=0; i<exc->cvtSize; ++i ) {
134 	    if ( dc->oldcvt[i]!=exc->cvt[i] ) {
135 		if ( dc->watchcvt[i] ) {
136 		    h = true;
137 		    dc->wp_cvtindex = i;
138 		    dc->oldcval = dc->oldcvt[i];
139 		}
140 		dc->oldcvt[i] = exc->cvt[i];
141 	    }
142 	}
143 	dc->found_wpc = h;
144 	hit |= h;
145     }
146 return( hit );
147 }
148 
AtBp(struct debugger_context * dc,TT_ExecContext exc)149 static int AtBp(struct debugger_context *dc, TT_ExecContext exc ) {
150     int i;
151 
152     if ( dc->temp.range==exc->curRange && dc->temp.ip==exc->IP ) {
153 	dc->temp.range = tt_coderange_none;
154 return( true );
155     }
156 
157     for ( i=0; i<dc->bcnt; ++i ) {
158 	if ( dc->breaks[i].range==exc->curRange && dc->breaks[i].ip==exc->IP )
159 return( true );
160     }
161 return( false );
162 }
163 
TestStorage(struct debugger_context * dc,TT_ExecContext exc)164 static void TestStorage( struct debugger_context *dc, TT_ExecContext exc) {
165     int instr;
166 
167     if ( exc->code==NULL || exc->IP==exc->codeSize )
168 return;
169     instr = exc->code[exc->IP];
170     if ( instr==0x42 /* Write store */ && exc->top>=2 ) {
171 	int store_index = exc->stack[exc->top-2];
172 	if ( store_index>=0 && store_index<exc->storeSize )
173 	    dc->storetouched[store_index] = 3;	/* 2=>written this instr, 1=>ever written */
174     } else if ( instr==0x43 /* Read Store */ && exc->top>=1 ) {
175 	int store_index = exc->stack[exc->top-1];
176 	if ( store_index>=0 && store_index<exc->storeSize &&
177 		!dc->storetouched[store_index] &&
178 		dc->watchstorage!=NULL && dc->watchstorage[store_index] ) {
179 	    dc->found_wps_uninit = true;
180 	    dc->uninit_index = store_index;
181 	}
182     }
183 }
184 
185 static struct debugger_context *massive_kludge;
186 
PauseIns(TT_ExecContext exc)187 static FT_Error PauseIns( TT_ExecContext exc ) {
188     int ret;
189     struct debugger_context *dc = massive_kludge;
190 
191     if ( dc->terminate )
192 return( TT_Err_Execution_Too_Long );		/* Some random error code, says we're probably in a infinite loop */
193     dc->exc = exc;
194     exc->grayscale = !dc->is_bitmap;		/* if we are in 'prep' or 'fpgm' freetype doesn't know this yet */
195 
196     /* Set up for watch points */
197     if ( dc->oldpts==NULL && exc->pts.n_points!=0 ) {
198 	dc->oldpts = calloc(exc->pts.n_points,sizeof(FT_Vector));
199 	dc->n_points = exc->pts.n_points;
200     }
201     if ( dc->oldstore==NULL && exc->storeSize!=0 ) {
202 	dc->oldstore = calloc(exc->storeSize,sizeof(FT_Long));
203 	dc->storetouched = calloc(exc->storeSize,sizeof(uint8));
204 	dc->storeSize = exc->storeSize;
205     }
206     if ( dc->oldcvt==NULL && exc->cvtSize!=0 )
207 	dc->oldcvt = calloc(exc->cvtSize,sizeof(FT_Long));
208     if ( !dc->initted_pts ) {
209 	AtWp(dc,exc);
210 	dc->found_wp = false;
211 	dc->found_wps = false;
212 	dc->found_wps_uninit = false;
213 	dc->found_wpc = false;
214 	dc->initted_pts = true;
215     }
216 
217     if ( !dc->debug_fpgm && exc->curRange!=tt_coderange_glyph ) {
218 	exc->instruction_trap = 1;
219 	ret = 0;
220 	while ( exc->curRange!=tt_coderange_glyph ) {
221 	    TestStorage(dc,exc);
222 	    ret = TT_RunIns(exc);
223 	    if ( ret==TT_Err_Code_Overflow )
224 return( 0 );
225 	    if ( ret )
226 return( ret );
227 	}
228 return( ret );
229     }
230 
231     pthread_mutex_lock(&dc->parent_mutex);
232     pthread_cond_signal(&dc->parent_cond);
233     pthread_mutex_unlock(&dc->parent_mutex);
234     pthread_cond_wait(&dc->child_cond,&dc->child_mutex);
235     if ( dc->terminate )
236 return( TT_Err_Execution_Too_Long );
237 
238     do {
239 	exc->instruction_trap = 1;
240 	if (exc->curRange==tt_coderange_glyph && exc->IP==exc->codeSize) {
241 	    ret = TT_Err_Code_Overflow;
242     break;
243 	}
244 	TestStorage(dc,exc);
245 	ret = TT_RunIns(exc);
246 	if ( ret )
247     break;
248 	/* Signal the parent if we are single stepping, or if we've reached a break-point */
249 	if ( AtWp(dc,exc) || !dc->multi_step || AtBp(dc,exc) ||
250 		(exc->curRange==tt_coderange_glyph && exc->IP==exc->codeSize)) {
251 	    if ( dc->found_wp ) {
252 		ff_post_notice(_("Hit Watch Point"),_("Point %d was moved by the previous instruction"),dc->wp_ptindex);
253 		dc->found_wp = false;
254 	    }
255 	    if ( dc->found_wps ) {
256 		ff_post_notice(_("Watched Store Change"),_("Storage %d was changed from %d (%.2f) to %d (%.2f) by the previous instruction"),
257 			dc->wp_storeindex, dc->oldsval, dc->oldsval/64.0,exc->storage[dc->wp_storeindex],exc->storage[dc->wp_storeindex]/64.0);
258 		dc->found_wps = false;
259 	    }
260 	    if ( dc->found_wps_uninit ) {
261 		ff_post_notice(_("Read of Uninitialized Store"),_("Storage %d has not been initialized, yet the previous instruction read it"),
262 			dc->uninit_index );
263 		dc->found_wps_uninit = false;
264 	    }
265 	    if ( dc->found_wpc ) {
266 		ff_post_notice(_("Watched Cvt Change"),_("Cvt %d was changed from %d (%.2f) to %d (%.2f) by the previous instruction"),
267 			dc->wp_cvtindex, dc->oldcval, dc->oldcval/64.0,exc->cvt[dc->wp_cvtindex],exc->cvt[dc->wp_cvtindex]/64.0);
268 		dc->found_wpc = false;
269 	    }
270 	    pthread_mutex_lock(&dc->parent_mutex);
271 	    pthread_cond_signal(&dc->parent_cond);
272 	    pthread_mutex_unlock(&dc->parent_mutex);
273 	    pthread_cond_wait(&dc->child_cond,&dc->child_mutex);
274 	}
275     } while ( !dc->terminate );
276 
277     if ( ret==TT_Err_Code_Overflow )
278 	ret = 0;
279 
280     massive_kludge = dc;	/* We set this again in case we are in a composite character where I think we get called several times (and some other thread might have set it) */
281     if ( dc->terminate )
282 return( TT_Err_Execution_Too_Long );
283 
284 return( ret );
285 }
286 
StartChar(void * _dc)287 static void *StartChar(void *_dc) {
288     struct debugger_context *dc = _dc;
289 
290     pthread_mutex_lock(&dc->child_mutex);
291 
292     massive_kludge = dc;
293     if ( (dc->ftc = __FreeTypeFontContext(dc->context,dc->sc->parent,dc->sc,NULL,
294 	    dc->layer,ff_ttf, 0, NULL))==NULL )
295  goto finish;
296     if ( dc->storetouched!=NULL )
297 	memset(dc->storetouched,0,dc->storeSize);
298 
299     massive_kludge = dc;
300     if ( FT_Set_Char_Size(dc->ftc->face,(int) (dc->ptsizex*64),(int) (dc->ptsizey*64), dc->dpi, dc->dpi))
301  goto finish;
302 
303     massive_kludge = dc;
304     FT_Load_Glyph(dc->ftc->face,dc->ftc->glyph_indeces[dc->sc->orig_pos],
305 	    dc->is_bitmap ? (FT_LOAD_NO_AUTOHINT|FT_LOAD_NO_BITMAP|FT_LOAD_TARGET_MONO) : (FT_LOAD_NO_AUTOHINT|FT_LOAD_NO_BITMAP));
306 
307  finish:
308     dc->has_finished = true;
309     dc->exc = NULL;
310     pthread_mutex_lock(&dc->parent_mutex);
311     pthread_cond_signal(&dc->parent_cond);	/* Wake up parent and get it to clean up after itself */
312     pthread_mutex_unlock(&dc->parent_mutex);
313     pthread_mutex_unlock(&dc->child_mutex);
314 return( NULL );
315 }
316 
DebuggerTerminate(struct debugger_context * dc)317 void DebuggerTerminate(struct debugger_context *dc) {
318     if ( dc->has_thread ) {
319 	if ( !dc->has_finished ) {
320 	    dc->terminate = true;
321 	    pthread_mutex_lock(&dc->child_mutex);
322 	    pthread_cond_signal(&dc->child_cond);	/* Wake up child and get it to clean up after itself */
323 	    pthread_mutex_unlock(&dc->child_mutex);
324 	    pthread_mutex_unlock(&dc->parent_mutex);
325 	}
326 	pthread_join(dc->thread,NULL);
327 	dc->has_thread = false;
328     }
329     if ( dc->has_mutexes ) {
330 	pthread_cond_destroy(&dc->child_cond);
331 	pthread_cond_destroy(&dc->parent_cond);
332 	pthread_mutex_destroy(&dc->child_mutex);
333 	pthread_mutex_unlock(&dc->parent_mutex);	/* Is this actually needed? */
334 	pthread_mutex_destroy(&dc->parent_mutex);
335     }
336     if ( dc->ftc!=NULL )
337 	FreeTypeFreeContext(dc->ftc);
338     if ( dc->context!=NULL )
339 	FT_Done_FreeType( dc->context );
340     free(dc->watch);
341     free(dc->oldpts);
342     free(dc);
343 }
344 
DebuggerReset(struct debugger_context * dc,real ptsizey,real ptsizex,int dpi,int dbg_fpgm,int is_bitmap)345 void DebuggerReset(struct debugger_context *dc,real ptsizey, real ptsizex,int dpi,int dbg_fpgm, int is_bitmap) {
346     /* Kill off the old thread, and start up a new one working on the given */
347     /*  pointsize and resolution */ /* I'm not prepared for errors here */
348     /* Note that if we don't want to look at the fpgm/prep code (and we */
349     /*  usually don't) then we must turn off the debug hook when they get run */
350 
351     if ( dc->has_thread ) {
352 	dc->terminate = true;
353 	pthread_mutex_lock(&dc->child_mutex);
354 	pthread_cond_signal(&dc->child_cond);	/* Wake up child and get it to clean up after itself */
355 	pthread_mutex_unlock(&dc->child_mutex);
356 	pthread_mutex_unlock(&dc->parent_mutex);
357 
358 	pthread_join(dc->thread,NULL);
359 	dc->has_thread = false;
360     }
361     if ( dc->ftc!=NULL )
362 	FreeTypeFreeContext(dc->ftc);
363 
364     dc->debug_fpgm = dbg_fpgm;
365     dc->ptsizey = ptsizey;
366     dc->ptsizex = ptsizex;
367     dc->dpi = dpi;
368     dc->is_bitmap = is_bitmap;
369     dc->terminate = dc->has_finished = false;
370     dc->initted_pts = false;
371 
372     pthread_mutex_lock(&dc->parent_mutex);
373     if ( pthread_create(&dc->thread,NULL,StartChar,(void *) dc)!=0 ) {
374 	DebuggerTerminate(dc);
375 return;
376     }
377     if ( dc->has_finished )
378 return;
379     dc->has_thread = true;
380     pthread_cond_wait(&dc->parent_cond,&dc->parent_mutex);
381 }
382 
DebuggerCreate(SplineChar * sc,int layer,real ptsizey,real ptsizex,int dpi,int dbg_fpgm,int is_bitmap)383 struct debugger_context *DebuggerCreate(SplineChar *sc,int layer, real ptsizey,real ptsizex,int dpi,int dbg_fpgm, int is_bitmap) {
384     struct debugger_context *dc;
385 
386     if ( !hasFreeTypeDebugger())
387 return( NULL );
388 
389     dc = calloc(1,sizeof(struct debugger_context));
390     dc->sc = sc;
391     dc->layer = layer;
392     dc->debug_fpgm = dbg_fpgm;
393     dc->ptsizey = ptsizey;
394     dc->ptsizex = ptsizex;
395     dc->dpi = dpi;
396     dc->is_bitmap = is_bitmap;
397     if ( FT_Init_FreeType( &dc->context )) {
398 	free(dc);
399 return( NULL );
400     }
401 
402 #if FREETYPE_MINOR >= 5
403     {
404 	int tt_version = TT_INTERPRETER_VERSION_35;
405 
406 	if ( FT_Property_Set( dc->context,
407 			      "truetype",
408 			      "interpreter-version",
409 			      &tt_version )) {
410 	    free(dc);
411 return( NULL );
412 	}
413     }
414 #endif
415 
416     FT_Set_Debug_Hook( dc->context,
417 		       FT_DEBUG_HOOK_TRUETYPE,
418 		       (FT_DebugHook_Func)PauseIns );
419 
420     pthread_mutex_init(&dc->parent_mutex,NULL); pthread_mutex_init(&dc->child_mutex,NULL);
421     pthread_cond_init(&dc->parent_cond,NULL); pthread_cond_init(&dc->child_cond,NULL);
422     dc->has_mutexes = true;
423 
424     pthread_mutex_lock(&dc->parent_mutex);
425     if ( pthread_create(&dc->thread,NULL,StartChar,dc)!=0 ) {
426 	DebuggerTerminate( dc );
427 return( NULL );
428     }
429     dc->has_thread = true;
430     pthread_cond_wait(&dc->parent_cond,&dc->parent_mutex);	/* Wait for the child to initialize itself (and stop) then we can look at its status */
431 
432 return( dc );
433 }
434 
DebuggerGo(struct debugger_context * dc,enum debug_gotype dgt,DebugView * dv)435 void DebuggerGo(struct debugger_context *dc,enum debug_gotype dgt,DebugView *dv) {
436     int opcode;
437 
438     if ( !dc->has_thread || dc->has_finished || dc->exc==NULL ) {
439 	FreeType_FreeRaster(dv->cv->raster); dv->cv->raster = NULL;
440 	DebuggerReset(dc,dc->ptsizey,dc->ptsizex,dc->dpi,dc->debug_fpgm,dc->is_bitmap);
441     } else {
442 	switch ( dgt ) {
443 	  case dgt_continue:
444 	    dc->multi_step = true;
445 	  break;
446 	  case dgt_stepout:
447 	    dc->multi_step = true;
448 	    if ( dc->exc->callTop>0 ) {
449 		dc->temp.range = dc->exc->callStack[dc->exc->callTop-1].Caller_Range;
450 		dc->temp.ip = dc->exc->callStack[dc->exc->callTop-1].Caller_IP;
451 	    }
452 	  break;
453 	  case dgt_next:
454 	    opcode = dc->exc->code[dc->exc->IP];
455 	    /* I've decided that IDEFs will get stepped into */
456 	    if ( opcode==0x2b /* call */ || opcode==0x2a /* loopcall */ ) {
457 		dc->temp.range = dc->exc->curRange;
458 		dc->temp.ip = dc->exc->IP+1;
459 		dc->multi_step = true;
460 	    } else
461 		dc->multi_step = false;
462 	  break;
463 	  default:
464 	  case dgt_step:
465 	    dc->multi_step = false;
466 	  break;
467 	}
468 	pthread_mutex_lock(&dc->child_mutex);
469 	pthread_cond_signal(&dc->child_cond);	/* Wake up child and get it to clean up after itself */
470 	pthread_mutex_unlock(&dc->child_mutex);
471 	pthread_cond_wait(&dc->parent_cond,&dc->parent_mutex);	/* Wait for the child to initialize itself (and stop) then we can look at its status */
472     }
473 }
474 
DebuggerGetEContext(struct debugger_context * dc)475 struct TT_ExecContextRec_ *DebuggerGetEContext(struct debugger_context *dc) {
476 return( dc->exc );
477 }
478 
DebuggerBpCheck(struct debugger_context * dc,int range,int ip)479 int DebuggerBpCheck(struct debugger_context *dc,int range,int ip) {
480     int i;
481 
482     for ( i=0; i<dc->bcnt; ++i ) {
483 	if ( dc->breaks[i].range==range && dc->breaks[i].ip==ip )
484 return( true );
485     }
486 return( false );
487 }
488 
DebuggerToggleBp(struct debugger_context * dc,int range,int ip)489 void DebuggerToggleBp(struct debugger_context *dc,int range,int ip) {
490     int i;
491 
492     /* If the address has a bp, then remove it */
493     for ( i=0; i<dc->bcnt; ++i ) {
494 	if ( dc->breaks[i].range==range && dc->breaks[i].ip==ip ) {
495 	    ++i;
496 	    while ( i<dc->bcnt ) {
497 		dc->breaks[i-1].range = dc->breaks[i].range;
498 		dc->breaks[i-1].ip = dc->breaks[i].ip;
499 		++i;
500 	    }
501 	    --dc->bcnt;
502 return;
503 	}
504     }
505     /* Else add it */
506     if ( dc->bcnt>=sizeof(dc->breaks)/sizeof(dc->breaks[0]) ) {
507 	ff_post_error(_("Too Many Breakpoints"),_("Too Many Breakpoints"));
508 return;
509     }
510     i = dc->bcnt++;
511     dc->breaks[i].range = range;
512     dc->breaks[i].ip = ip;
513 }
514 
DebuggerSetWatches(struct debugger_context * dc,int n,uint8 * w)515 void DebuggerSetWatches(struct debugger_context *dc,int n, uint8 *w) {
516     free(dc->watch); dc->watch=NULL;
517     if ( n!=dc->n_points ) IError("Bad watchpoint count");
518     else {
519 	dc->watch = w;
520 	if ( dc->exc ) {
521 	    AtWp(dc,dc->exc);
522 	    dc->found_wp = false;
523 	    dc->found_wpc = false;
524 	    dc->found_wps = false;
525 	    dc->found_wps_uninit = false;
526 	}
527     }
528 }
529 
DebuggerGetWatches(struct debugger_context * dc,int * n)530 uint8 *DebuggerGetWatches(struct debugger_context *dc, int *n) {
531     *n = dc->n_points;
532 return( dc->watch );
533 }
534 
DebuggerSetWatchStores(struct debugger_context * dc,int n,uint8 * w)535 void DebuggerSetWatchStores(struct debugger_context *dc,int n, uint8 *w) {
536     free(dc->watchstorage); dc->watchstorage=NULL;
537     if ( n!=dc->exc->storeSize ) IError("Bad watchpoint count");
538     else {
539 	dc->watchstorage = w;
540 	if ( dc->exc ) {
541 	    AtWp(dc,dc->exc);
542 	    dc->found_wp = false;
543 	    dc->found_wpc = false;
544 	    dc->found_wps = false;
545 	    dc->found_wps_uninit = false;
546 	}
547     }
548 }
549 
DebuggerGetWatchStores(struct debugger_context * dc,int * n)550 uint8 *DebuggerGetWatchStores(struct debugger_context *dc, int *n) {
551     *n = dc->exc->storeSize;
552 return( dc->watchstorage );
553 }
554 
DebuggerIsStorageSet(struct debugger_context * dc,int index)555 int DebuggerIsStorageSet(struct debugger_context *dc, int index) {
556     if ( dc->storetouched==NULL )
557 return( false );
558 return( dc->storetouched[index]&1 );
559 }
560 
DebuggerSetWatchCvts(struct debugger_context * dc,int n,uint8 * w)561 void DebuggerSetWatchCvts(struct debugger_context *dc,int n, uint8 *w) {
562     free(dc->watchcvt); dc->watchcvt=NULL;
563     if ( n!=dc->exc->cvtSize ) IError("Bad watchpoint count");
564     else {
565 	dc->watchcvt = w;
566 	if ( dc->exc ) {
567 	    AtWp(dc,dc->exc);
568 	    dc->found_wp = false;
569 	    dc->found_wpc = false;
570 	    dc->found_wps = false;
571 	    dc->found_wps_uninit = false;
572 	}
573     }
574 }
575 
DebuggerGetWatchCvts(struct debugger_context * dc,int * n)576 uint8 *DebuggerGetWatchCvts(struct debugger_context *dc, int *n) {
577     *n = dc->exc->cvtSize;
578 return( dc->watchcvt );
579 }
580 
DebuggingFpgm(struct debugger_context * dc)581 int DebuggingFpgm(struct debugger_context *dc) {
582 return( dc->debug_fpgm );
583 }
584 
DebuggerCurrentRaster(TT_ExecContext exc,int depth)585 struct freetype_raster *DebuggerCurrentRaster(TT_ExecContext exc,int depth) {
586     FT_Outline outline;
587     FT_Bitmap bitmap;
588     int i, err, j, k, first, xoff, yoff;
589     IBounds b;
590     struct freetype_raster *ret;
591 
592     outline.n_contours = exc->pts.n_contours;
593     outline.tags = (char *) exc->pts.tags;
594     outline.contours = (short *) exc->pts.contours;
595     /* Rasterizer gets unhappy if we give it the phantom points */
596     if ( outline.n_contours==0 )
597 	outline.n_points = 0;
598     else
599 	outline.n_points = /*exc->pts.n_points*/  outline.contours[outline.n_contours - 1] + 1;
600     outline.points = exc->pts.cur;
601     outline.flags = 0;
602     switch ( exc->GS.scan_type ) {
603       /* Taken, at Werner's suggestion, from the freetype sources: ttgload.c:1970 */
604       case 0: /* simple drop-outs including stubs */
605 	outline.flags |= FT_OUTLINE_INCLUDE_STUBS;
606       break;
607       case 1: /* simple drop-outs excluding stubs */
608 	/* nothing; it's the default rendering mode */
609       break;
610       case 4: /* smart drop-outs including stubs */
611 	outline.flags |= FT_OUTLINE_SMART_DROPOUTS |
612 				FT_OUTLINE_INCLUDE_STUBS;
613       break;
614       case 5: /* smart drop-outs excluding stubs  */
615 	outline.flags |= FT_OUTLINE_SMART_DROPOUTS;
616       break;
617 
618       default: /* no drop-out control */
619 	outline.flags |= FT_OUTLINE_IGNORE_DROPOUTS;
620       break;
621     }
622 
623     if ( exc->metrics.y_ppem < 24 )
624        outline.flags |= FT_OUTLINE_HIGH_PRECISION;
625 
626     first = true;
627     for ( k=0; k<outline.n_contours; ++k ) {
628 	if ( outline.contours[k] - (k==0?-1:outline.contours[k-1])>1 ) {
629 	    /* Single point contours are used for things like point matching */
630 	    /*  for anchor points, etc. and do not contribute to the bounding */
631 	    /*  box */
632 	    i = (k==0?0:(outline.contours[k-1]+1));
633 	    if ( first ) {
634 		b.minx = b.maxx = outline.points[i].x;
635 		b.miny = b.maxy = outline.points[i++].y;
636 		first = false;
637 	    }
638 	    for ( ; i<=outline.contours[k]; ++i ) {
639 		if ( outline.points[i].x>b.maxx ) b.maxx = outline.points[i].x;
640 		if ( outline.points[i].x<b.minx ) b.minx = outline.points[i].x;
641 		if ( outline.points[i].y>b.maxy ) b.maxy = outline.points[i].y;
642 		if ( outline.points[i].y<b.miny ) b.miny = outline.points[i].y;
643 	    }
644 	}
645     }
646     if ( first )
647 	memset(&b,0,sizeof(b));
648 
649     memset(&bitmap,0,sizeof(bitmap));
650     bitmap.rows = (((int) (ceil(b.maxy/64.0)-floor(b.miny/64.0)))) +1;
651     bitmap.width = (((int) (ceil(b.maxx/64.0)-floor(b.minx/64.0)))) +1;
652 
653     xoff = 64*floor(b.minx/64.0);
654     yoff = 64*floor(b.miny/64.0);
655     for ( i=0; i<outline.n_points; ++i ) {
656 	outline.points[i].x -= xoff;
657 	outline.points[i].y -= yoff;
658     }
659 
660     if ( depth==8 ) {
661 	bitmap.pitch = bitmap.width;
662 	bitmap.num_grays = 256;
663 	bitmap.pixel_mode = ft_pixel_mode_grays;
664     } else {
665 	bitmap.pitch = (bitmap.width+7)>>3;
666 	bitmap.num_grays = 0;
667 	bitmap.pixel_mode = ft_pixel_mode_mono;
668     }
669     bitmap.buffer = calloc(bitmap.pitch*bitmap.rows,sizeof(uint8));
670 
671     err = (FT_Outline_Get_Bitmap)(ff_ft_context,&outline,&bitmap);
672 
673     for ( i=0; i<outline.n_points; ++i ) {
674 	outline.points[i].x += xoff;
675 	outline.points[i].y += yoff;
676     }
677 
678     ret = malloc(sizeof(struct freetype_raster));
679     /* I'm not sure why I need these, but it seems I do */
680     if ( depth==8 ) {
681 	ret->as = floor(b.miny/64.0) + bitmap.rows;
682 	ret->lb = floor(b.minx/64.0);
683     } else {
684 	for ( k=0; k<bitmap.rows; ++k ) {
685 	    for ( j=bitmap.pitch-1; j>=0 && bitmap.buffer[k*bitmap.pitch+j]==0; --j );
686 	    if ( j!=-1 )
687 	break;
688 	}
689 	b.maxy += k<<6;
690 	if ( depth==8 ) {
691 	    for ( j=0; j<bitmap.pitch; ++j ) {
692 		for ( k=(((int) (b.maxy-b.miny))>>6)-1; k>=0; --k ) {
693 		    if ( bitmap.buffer[k*bitmap.pitch+j]!=0 )
694 		break;
695 		}
696 		if ( k!=-1 )
697 	    break;
698 	    }
699 	} else {
700 	    for ( j=0; j<bitmap.pitch; ++j ) {
701 		for ( k=(((int) (b.maxy-b.miny))>>6)-1; k>=0; --k ) {
702 		    if ( bitmap.buffer[k*bitmap.pitch+(j>>3)]&(0x80>>(j&7)) )
703 		break;
704 		}
705 		if ( k!=-1 )
706 	    break;
707 	    }
708 	}
709 	b.minx -= j*64;
710 	ret->as = rint(b.maxy/64.0);
711 	ret->lb = rint(b.minx/64.0);
712     }
713     ret->rows = bitmap.rows;
714     ret->cols = bitmap.width;
715     ret->bytes_per_row = bitmap.pitch;
716     ret->num_greys = bitmap.num_grays;
717     ret->bitmap = bitmap.buffer;
718 return( ret );
719 }
720 
721 #else /* FIXME: Don't build this stuff if it's not being used, it just makes the compiler emit lots of warnings */
722 struct debugger_context;
723 
DebuggerTerminate(struct debugger_context * dc)724 void DebuggerTerminate(struct debugger_context *dc) {
725 }
726 
DebuggerReset(struct debugger_context * dc,real ptsizey,real ptsizex,int dpi,int dbg_fpgm,int is_bitmap)727 void DebuggerReset(struct debugger_context *dc,real ptsizey, real ptsizex,int dpi,int dbg_fpgm, int is_bitmap) {
728 }
729 
DebuggerCreate(SplineChar * sc,int layer,real pointsizey,real pointsizex,int dpi,int dbg_fpgm,int is_bitmap)730 struct debugger_context *DebuggerCreate(SplineChar *sc,int layer,real pointsizey, real pointsizex,int dpi, int dbg_fpgm, int is_bitmap) {
731 return( NULL );
732 }
733 
DebuggerGo(struct debugger_context * dc,enum debug_gotype go,DebugView * dv)734 void DebuggerGo(struct debugger_context *dc,enum debug_gotype go,DebugView *dv) {
735 }
736 
DebuggerGetEContext(struct debugger_context * dc)737 struct TT_ExecContextRec_ *DebuggerGetEContext(struct debugger_context *dc) {
738 return( NULL );
739 }
740 
DebuggerSetWatches(struct debugger_context * dc,int n,uint8 * w)741 void DebuggerSetWatches(struct debugger_context *dc,int n, uint8 *w) {
742 }
743 
DebuggerGetWatches(struct debugger_context * dc,int * n)744 uint8 *DebuggerGetWatches(struct debugger_context *dc, int *n) {
745     *n = 0;
746 return( NULL );
747 }
748 
DebuggerSetWatchStores(struct debugger_context * dc,int n,uint8 * w)749 void DebuggerSetWatchStores(struct debugger_context *dc,int n, uint8 *w) {
750 }
751 
DebuggerGetWatchStores(struct debugger_context * dc,int * n)752 uint8 *DebuggerGetWatchStores(struct debugger_context *dc, int *n) {
753     *n = 0;
754 return( NULL );
755 }
756 
DebuggerIsStorageSet(struct debugger_context * dc,int index)757 int DebuggerIsStorageSet(struct debugger_context *dc, int index) {
758 return( false );
759 }
760 
DebuggerSetWatchCvts(struct debugger_context * dc,int n,uint8 * w)761 void DebuggerSetWatchCvts(struct debugger_context *dc,int n, uint8 *w) {
762 }
763 
DebuggerGetWatchCvts(struct debugger_context * dc,int * n)764 uint8 *DebuggerGetWatchCvts(struct debugger_context *dc, int *n) {
765     *n = 0;
766 return( NULL );
767 }
768 
DebuggingFpgm(struct debugger_context * dc)769 int DebuggingFpgm(struct debugger_context *dc) {
770 return( false );
771 }
772 #endif	/* FREETYPE_HAS_DEBUGGER */
773