rem: | kATSLineUseDeviceMetrics); #endif
add: | kATSLineUseDeviceMetrics | (use_cgctx ? 0 : kATSLineUseQDRendering));
context: static double DrawMeasUnicodeText(const char *text, int d, int theStrlen, int ucs4,				  int just_meas, int given_font, 				  short txFont, short txSize, short txFace,				  int again, int qd_spacing, int smoothing,				  double angle, int is_sym,				  double scale_x, double scale_y,				  double pen_delta, int use_pen_delta,				  double start_x, double start_y, double ddx, double ddy, int with_start){  ATSUTextLayout layout = NULL, *layouts;  UniCharCount ulen, one_ulen, delta;  UniChar *unicode;  double result = 0, one_res = 0;  int need_convert, need_layout, need_size, textMode = 0;  Scheme_Object *val;#define JUSTDELTA(v, s, d) (need_convert ? v : ((v - d) / s))#define COORDCONV(v, s, d) (need_convert ? ((v * s) + d) : v)  CGrafPtr qdp;  CGContextRef cgctx;  Rect portRect;  RGBColor eraseColor, textColor;  ATSUStyle style;  Point start;  RgnHandle clipRgn;  double *widths;  int use_cgctx = (always_use_atsu 		   && ((smoothing != wxSMOOTHING_PARTIAL) || (scale_x != scale_y)));  int use_cache = (qd_spacing && scheme_current_thread);  int *glyphs;  FontInfo fontInfo;	  if (!theATSUstyle)    init_ATSU_style();  START_TIME;  /****************************************/  /* Unicode conversion                  */  if (ucs4) {    int i, extra;    unsigned int v;    UniCharCount alloc_ulen;    /* Count characters that fall outside UCS-2: */    for (i = 0, extra = 0; i < theStrlen; i++) {      if (((unsigned int *)text)[d+i] > 0xFFFF)	extra++;    }    ulen = theStrlen + extra;    alloc_ulen = ulen;    if (alloc_ulen > QUICK_UBUF_SIZE)      unicode = new WXGC_ATOMIC UniChar[alloc_ulen];    else      unicode = u_buf;        /* UCS-4 -> UTF-16 conversion */    for (i = 0, extra = 0; i < theStrlen; i++) {      v = ((unsigned int *)text)[d+i];      if (v > 0xFFFF) {	v -= 0x10000;	unicode[i+extra] = 0xD800 | ((v >> 10) & 0x3FF);	extra++;	unicode[i+extra] = 0xDC00 | (v & 0x3FF);      } else	unicode[i+extra] = v;    }  } else {    UniCharCount alloc_ulen;    /* UTF-8 -> UTF-16 conversion */    ulen = scheme_utf8_decode((unsigned char *)text, d, 			      theStrlen, NULL, 0, -1, 			      NULL, 1 /*UTF-16*/, '?');    alloc_ulen = ulen;    if (alloc_ulen > QUICK_UBUF_SIZE)      unicode = new WXGC_ATOMIC UniChar[alloc_ulen];    else      unicode = u_buf;    ulen = scheme_utf8_decode((unsigned char *)text, d, theStrlen, 			      (unsigned int *)unicode, 0, -1, 			      NULL, 1 /*UTF-16*/, '?');  }  if (is_sym == wxSYMBOL) {    unsigned int i;    int v, m;    for (i = 0; i < ulen; i++) {      v = unicode[d+i];      if (v < 256) {	m = symbol_map[v];	unicode[i] = (m ? m : v);      }    }  }  END_TIME(preprocess);  START_TIME;  GetPort(&qdp);  /****************************************/  /* Set up measure cache                 */  if (!width_table) {    char *s;    wxREGGLOB(style_table);    wxREGGLOB(width_table);    wxREGGLOB(old_width_table);    wxREGGLOB(table_key);    style_table = scheme_make_hash_table_equal();    width_table = scheme_make_hash_table_equal();    old_width_table = scheme_make_hash_table_equal();    s = new WXGC_ATOMIC char[sizeof(wxKey)];    memset(s, 0, sizeof(wxKey));    table_key = scheme_make_sized_byte_string(s, sizeof(wxKey), 0);  }  if (!given_font) {    txFont = GetPortTextFont(qdp);    txSize = GetPortTextSize(qdp);    txFace = GetPortTextFace(qdp);  }  {    wxKey *k = (wxKey *)SCHEME_BYTE_STR_VAL(table_key);    k->scale_x = scale_x;    k->scale_y = scale_y;    k->angle = angle;    k->txFont = txFont;    k->txSize = txSize;    k->txFace = txFace;    k->use_cgctx = (char)use_cgctx;    k->smoothing = (char)smoothing;  }  if (use_cache) {    /* Get all cached sizes */    double r = 0;    int i, all = 1, wc, di;    atomic_timeout_t old;    if (ulen > QUICK_UBUF_SIZE) {      widths = new WXGC_ATOMIC double[ulen];      layouts = (ATSUTextLayout *)(new WXGC_ATOMIC char[sizeof(ATSUTextLayout) * ulen]);      glyphs = new WXGC_ATOMIC int[ulen];    } else {      widths = widths_buf;      layouts = layout_buf;      glyphs = glyphs_buf;    }    old = pre_scheme();    for (i = 0; i < (int)ulen; i += di) {      wc = unicode[i];      if ((wc & 0xF800) == 0xD800) {	/* Yuck. Re-un-parse UTF-16... */	wc = ((wc & 0x3FF) << 10) + (unicode[i+1] & 0x3FF);	di = 2;      } else	di = 1;            ((wxKey *)SCHEME_BYTE_STR_VAL(table_key))->code = wc;      val = lookup_width(table_key);      if (!val) {	all = 0;	widths[i] = -1;      } else {	widths[i] = SCHEME_DBL_VAL(SCHEME_VEC_ELS(val)[0]);	layouts[i] = *(ATSUTextLayout *)SCHEME_VEC_ELS(val)[1];	glyphs[i] = SCHEME_INT_VAL(SCHEME_VEC_ELS(val)[2]);	r += widths[i];      }    }    post_scheme(old);    if (all && just_meas)      return r;  } else {    widths = NULL;    layouts = NULL;    glyphs = NULL;  }  END_TIME(cache);  START_TIME;    /****************************************/  /* Set up style                         */  /* We cache ATSUStyles using a hash table, and currently, we never     release them. (In case some adds relasing code later: check     whether it's ok to release a style that was provided to a     text-layout object. */  if (use_cache) {    atomic_timeout_t old;    old = pre_scheme();    ((wxKey *)SCHEME_BYTE_STR_VAL(table_key))->angle = 0.0;    ((wxKey *)SCHEME_BYTE_STR_VAL(table_key))->code = 0;    val = scheme_hash_get(style_table, table_key);    if (val) {      style = *(ATSUStyle *)val;    } else {      Scheme_Object *new_key;      ATSUCreateAndCopyStyle(theATSUqdstyle, &style);      atsuSetStyleFromGrafPtrParams(style, txFont, txSize, txFace, smoothing, 				    angle, use_cgctx ? 1.0 : scale_y,				    qd_spacing);      val = (Scheme_Object *)(new WXGC_ATOMIC char[sizeof(ATSUStyle)]);      *(ATSUStyle *)val = style;      new_key = scheme_make_sized_byte_string(SCHEME_BYTE_STR_VAL(table_key), sizeof(wxKey), 1);      scheme_hash_set(style_table, new_key, val);    }    ((wxKey *)SCHEME_BYTE_STR_VAL(table_key))->angle = angle;    post_scheme(old);  } else {    style = theATSUstyle;    atsuSetStyleFromGrafPtrParams(style, txFont, txSize, txFace, smoothing, 				  angle, use_cgctx ? 1.0 : scale_y,				  qd_spacing);  }  END_TIME(style);  START_TIME;    /****************************************/  /* Set up port                          */  if (!just_meas) {    GetPortBounds(qdp, &portRect);     GetBackColor(&eraseColor);    GetForeColor(&textColor);    textMode = GetPortTextMode(qdp);    if (!with_start) {      GetPen(&start);      start_x = start.h;      start_y = start.v;    }    if ((angle == 0.0) && (textMode == srcCopy)) {      ::GetFontInfo(&fontInfo);    }    if (use_cgctx) {      /* Make clipping regions match (including BeginUpdate effect) */      clipRgn = NewRgn();      if (clipRgn) {	RgnHandle visRgn;	visRgn = NewRgn();	if (visRgn) {	  GetPortClipRegion(qdp, clipRgn);	  GetPortVisibleRegion(qdp, visRgn);	  SectRgn(clipRgn, visRgn, clipRgn);	  DisposeRgn(visRgn);	}      }    } else      clipRgn = NULL;  } else    clipRgn = NULL;  if (use_cgctx && QDBeginCGContext(qdp, &cgctx))    use_cgctx = 0;  if (use_cgctx && !just_meas) {    SyncCGContextOriginWithPort(cgctx, qdp);    if (clipRgn) {      ClipCGContextToRegion(cgctx, &portRect, clipRgn);      DisposeRgn(clipRgn);    }  }  if (use_cgctx) {    /* Set scale */    if (!just_meas) {      long h;      h = portRect.bottom - portRect.top;      CGContextTranslateCTM(cgctx, 			    (with_start ? ddx : 0), 			    h - (with_start ? ddy : 0));      ddx = 0;      ddy = 0;    }    CGContextScaleCTM(cgctx, scale_x, scale_y);    /* set color */    CGContextSetRGBFillColor(cgctx, 			     (double)textColor.red / 65535.0,			     (double)textColor.green / 65535.0,			     (double)textColor.blue / 65535.0,			     1.0);  }  END_TIME(ctx);  START_TIME;  /****************************************/  /* Draw/measure loop                    */    /* Beware of GCing without adjusting the text layout, because the  */  /* unicode string could move, and because GCing might attempt to   */  /* draw a bitmap into the same port. */  delta = 0;	  while (1) {    if (delta >= ulen)      break;    if (qd_spacing) {      if ((unicode[delta] & 0xF800) == 0xD800) {	/* Surrogate pair; need to handle 2 at a time */	one_ulen = 2;      } else	one_ulen = 1;    } else      one_ulen = ulen;    if (use_cache) {      if (widths[delta] >= 0) {	one_res = widths[delta];	layout = layouts[delta];	need_size = 0;	/* Adjust context in layout: */	if (use_cgctx) {	  GC_CAN_IGNORE ATSUAttributeTag ll_theTags[1];	  GC_CAN_IGNORE ByteCount ll_theSizes[1];	  ATSUAttributeValuePtr ll_theValues[1];	  ll_theTags[0] = kATSUCGContextTag;	  ll_theSizes[0] = sizeof(CGContextRef);	  ll_theValues[0] =  &cgctx;	  ATSUSetLayoutControls(layout, 1, ll_theTags, ll_theSizes, ll_theValues);	}      } else {	one_res = 0;	need_size = 1;      }    } else {      need_size = 1;      one_res = 0;    }    need_layout = (!just_meas || need_size);    if (need_layout) {      if (!layout) {	UniCharArrayPtr uca;	ATSUStyle *style_array;	UniCharCount *ulen_array;	if (use_cache) {	  uca = (UniCharArrayPtr)malloc(one_ulen * sizeof(UniChar) + sizeof(ATSUStyle) + sizeof(UniCharCount));	  memcpy(uca, unicode XFORM_OK_PLUS delta, one_ulen * sizeof(UniChar));	  style_array = (ATSUStyle *)((char *)uca XFORM_OK_PLUS (one_ulen * sizeof(UniChar)));	  ulen_array = (UniCharCount *)(style_array XFORM_OK_PLUS 1);	  *style_array = style;	  *ulen_array = one_ulen;	} else {	  uca = (UniCharArrayPtr)(unicode XFORM_OK_PLUS delta);	  style_array = &style;	  ulen_array = &one_ulen;	}	ATSUCreateTextLayoutWithTextPtr(uca,					kATSUFromTextBeginning,					kATSUToTextEnd,					one_ulen,					1,					ulen_array,					style_array,					&layout);	if (qd_spacing || use_cgctx) {	  int cnt = 0;	  GC_CAN_IGNORE ATSUAttributeTag ll_theTags[2];	  GC_CAN_IGNORE ByteCount ll_theSizes[2];	  ATSUAttributeValuePtr ll_theValues[2];	  ATSLineLayoutOptions ll_attribs;	  if (qd_spacing) {#if 1	    /* We write down a literal constant, because the constants aren't	       in 10.1 */	    ll_attribs = 0x11f4040;#else	    ll_attribs = (kATSLineFractDisable 			  | kATSLineDisableAutoAdjustDisplayPos			  | kATSLineDisableAllLayoutOperations			  | kATSLineUseDeviceMetrics);#endif	    ll_theTags[cnt] = kATSULineLayoutOptionsTag;	    ll_theSizes[cnt] = sizeof(ATSLineLayoutOptions);	    ll_theValues[cnt] = &ll_attribs;	    cnt++;	  }	  if (use_cgctx) {	    ll_theTags[cnt] = kATSUCGContextTag;	    ll_theSizes[cnt] = sizeof(CGContextRef);	    ll_theValues[cnt] =  &cgctx;	    cnt++;	  }    	  ATSUSetLayoutControls(layout, cnt, ll_theTags, ll_theSizes, ll_theValues);	}	ATSUSetTransientFontMatching(layout, TRUE);      	if (angle != 0.0) {	  GC_CAN_IGNORE ATSUAttributeTag  r_theTags[] = { kATSULineRotationTag };	  GC_CAN_IGNORE ByteCount    r_theSizes[] = { sizeof(Fixed) };	  ATSUAttributeValuePtr r_theValues[1];	  Fixed deg_angle;		  deg_angle = DoubleToFixed(angle * 180 / 3.14159);	  r_theValues[0] = &deg_angle;	  ATSUSetLayoutControls(layout, 1, r_theTags, r_theSizes, r_theValues); 	}      }    }          END_TIME(layout);    START_TIME;    if (need_size) {      ATSTrapezoid bounds;      ItemCount actual;      ATSUTextLayout meas_layout;      if (angle != 0.0) {	ATSUAttributeTag tag = kATSULineRotationTag;	ATSUCreateAndCopyTextLayout(layout, &meas_layout);	ATSUClearLayoutControls(meas_layout, 1, &tag);      } else	meas_layout = layout;      ATSUGetGlyphBounds(meas_layout,			 0, 0,			 kATSUFromTextBeginning,			 kATSUToTextEnd,			 kATSUseDeviceOrigins,			 1,			 &bounds,			 &actual);      if (angle != 0.0)	ATSUDisposeTextLayout(meas_layout);          one_res = (Fix2X(bounds.upperRight.x) - Fix2X(bounds.upperLeft.x));      if (one_res < 0)	one_res = 0;      if (use_cache)	widths[delta] = one_res;    } else if (use_cache)      widths[delta] = -1.0; /* inidicates that we don't need to re-hash */    if (!use_cgctx && !just_meas) {      one_res = one_res / scale_y;    }    result += one_res;    END_TIME(measure);    START_TIME;    if (!just_meas) {      if (!with_start) {	ddx = 0;	ddy = 0;	need_convert = 0;      } else {	need_convert = 1;      }          if ((angle == 0.0) && (textMode == srcCopy)) {	if (use_cgctx) {	  CGRect cgr;	  double rt, rl, rr, rb;	  rl = JUSTDELTA(start_x, scale_x, ddx) + (use_pen_delta ? pen_delta : 0.0);	  rt = JUSTDELTA(start_y, scale_y, ddy) - fontInfo.ascent;	  rb = JUSTDELTA(start_y, scale_y, ddy) + fontInfo.descent;	  rr = rl + one_res;	  cgr.origin.x = rl;	  cgr.origin.y = -rb;	  cgr.size.width = rr - rl;	  cgr.size.height = rb - rt;	  CGContextSetRGBFillColor(cgctx, 				   (double)eraseColor.red / 65535.0,				   (double)eraseColor.green / 65535.0,				   (double)eraseColor.blue / 65535.0,				   1.0);	  CGContextFillRect(cgctx, cgr);	  CGContextSetRGBFillColor(cgctx, 				   (double)textColor.red / 65535.0,				   (double)textColor.green / 65535.0,				   (double)textColor.blue / 65535.0,				   1.0);	} else {	  Rect theRect;	  double rt, rl, rr, rb;	  	  rl = COORDCONV(start_x, scale_x, ddx) + (use_pen_delta ? (pen_delta * scale_x) : 0.0);	  rt = COORDCONV(start_y, scale_y, ddy) - (fontInfo.ascent * scale_y);	  rb = COORDCONV(start_y, scale_y, ddy) + (fontInfo.descent * scale_y);	  rr = rl + (one_res * scale_x);	  theRect.left = (int)floor(rl);	  theRect.top = (int)floor(rt);	  theRect.right = (int)floor(rr);	  theRect.bottom = (int)floor(rb);	  EraseRect(&theRect);	}      }          {	Fixed sx, sy;	if (use_cgctx) {	  double isx;	  isx = JUSTDELTA(start_x, scale_x, ddx) + (use_pen_delta ? pen_delta : 0.0);	  sx = DoubleToFixed(isx);	} else if (with_start) {	  double isx;	  isx = COORDCONV(start_x, scale_x, ddx) + (use_pen_delta ? (pen_delta * scale_x) : 0.0);	  sx = DoubleToFixed(isx);	} else {	  sx = kATSUUseGrafPortPenLoc;	}	if (use_cgctx) {	  double isy;	  isy = -JUSTDELTA(start_y, scale_y, ddy);	  sy = DoubleToFixed(isy);	} else if (with_start) {	  double isy;	  isy = COORDCONV(start_y, scale_y, ddy);	  sy = DoubleToFixed(isy);	} else {	  sy = kATSUUseGrafPortPenLoc;	}	ATSUDrawText(layout, 		     kATSUFromTextBeginning,		     kATSUToTextEnd,		     sx, sy);      }    }    if (!with_start) {      /* Make sure start is scaled for further iterations */      start_x = scale_x * start_x;      start_y = scale_y * start_y;      with_start = 1;    }    if (angle == 0.0) {      start_x += one_res;    } else {      start_x += one_res * cos(angle);      start_y -= one_res * sin(angle);    }    if (use_cache) {      if (need_size) {	ItemCount n;	ATSLayoutRecord *info;	if (ATSUDirectGetLayoutDataArrayPtrFromTextLayout(layout, 							  0, 							  kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,							  (void **)&info, &n)	    == noErr) {	  if (n == 2) {	    /* Make sure that the glyph was not substituted from another font: */	    ATSUFontID fontid;	    UniCharArrayOffset changed;	    UniCharCount changedLen;		    if (ATSUMatchFontsToText(layout,				     kATSUFromTextBeginning,				     kATSUToTextEnd,				     &fontid,				     &changed,				     &changedLen)		== noErr) {	      glyphs[delta] = info->glyphID;	    } else {	      glyphs[delta] = -1;	    }	  } else {	    glyphs[delta] = -1;	  }	  ATSUDirectReleaseLayoutDataArrayPtr(NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&info);	} else {	  glyphs[delta] = -1;	}	layouts[delta] = layout;      }      layout = NULL;    } else if (qd_spacing) {      if (layout)	ATSUDisposeTextLayout(layout);      layout = NULL;    }    delta += one_ulen;  }  if (layout)    ATSUDisposeTextLayout(layout);  if (!just_meas) {    if (use_cgctx) {      CGContextSynchronize(cgctx);      QDEndCGContext(qdp, &cgctx);    }    /* QuickDraw is back again: */    if (!just_meas && !use_pen_delta && !with_start)      MoveTo(start.h + (int)floor(result * scale_x), start.v);  } else {    if (use_cgctx) {      QDEndCGContext(qdp, &cgctx);    }  }  if (use_cache) {    /* Record collected widths. (We can't record these during the       drawing loop because it might trigger a GC, which might try to       draw a GC bitmap, etc. */    int j, wc, dj;    atomic_timeout_t old;    old = pre_scheme();    for (j = 0; j < (int)ulen; j += dj) {      wc = unicode[j];      if ((wc & 0xF800) == 0xD800) {	/* Yuck. Re-un-parse UTF-16... */	wc = ((wc & 0x3FF) << 10) + (unicode[j+1] & 0x3FF);	dj = 2;      } else	dj = 1;      if (widths[j] >= 0) {	char *lp;	Scheme_Object *vec, *wd;	val = scheme_make_sized_byte_string(SCHEME_BYTE_STR_VAL(table_key), sizeof(wxKey), 1);	((wxKey *)SCHEME_BYTE_STR_VAL(val))->code = wc;	vec = scheme_make_vector(3, NULL);	wd = scheme_make_double(widths[j]);	lp = new WXGC_ATOMIC char[sizeof(ATSUTextLayout)];	*(ATSUTextLayout*)lp = layouts[j];	SCHEME_VEC_ELS(vec)[0] = wd;	SCHEME_VEC_ELS(vec)[1] = (Scheme_Object *)lp;	SCHEME_VEC_ELS(vec)[2] = scheme_make_integer(glyphs[j]);	scheme_hash_set(width_table, val, vec);		if (width_table->mcount >= MAX_WIDTH_MAPPINGS) {	  /* rotate tables, so width_table doesn't grow indefinitely,	     but we also don't throw away recent information completely.	     the old old_widths_table is going away, so dispose of	     information there. */	  {	    int i;	    void *ptr;	    Boolean is_hand;	    UniCharArrayOffset poffset;	    UniCharCount plen, total_plen;	    for (i = old_width_table->size; i--; ) {	      if (old_width_table->vals[i]) {		layout = *(ATSUTextLayout *)(SCHEME_VEC_ELS(old_width_table->vals[i])[1]);		ATSUGetTextLocation(layout,				    &ptr,				    &is_hand,				    &poffset,				    &plen,				    &total_plen);		ATSUDisposeTextLayout(layout);		free(ptr);	      }	    }	  }	  old_width_table = width_table;	  width_table = scheme_make_hash_table_equal();	}      }    }    post_scheme(old);  }  END_TIME(draw);  #if 0  if (!((time_counter++) & 0xFF)) {    printf("---%ld\npre %ld\nctx %ld\nstyle %ld\nlayout %ld\nmeasure %ld\ndraw %ld\n",	   time_counter,	   time_preprocess, time_ctx, time_style, time_layout, time_measure, time_draw);  }#endif  return result;}