Beispiel #1
0
CGColorRef createCGColor(const Color& c)
{
    CGColorRef color = NULL;
    CMProfileRef prof = NULL;
    CMGetSystemProfile(&prof);

    RetainPtr<CGColorSpaceRef> rgbSpace(AdoptCF, CGColorSpaceCreateWithPlatformColorSpace(prof));

    if (rgbSpace) {
        CGFloat components[4] = { static_cast<CGFloat>(c.red()) / 255, static_cast<CGFloat>(c.green()) / 255,
                                  static_cast<CGFloat>(c.blue()) / 255, static_cast<CGFloat>(c.alpha()) / 255 };
        color = CGColorCreate(rgbSpace.get(), components);
    }

    CMCloseProfile(prof);

    return color;
}
Beispiel #2
0
//-----------------------------------------------------------------------------
CGColorRef getCGColor (const CColor& color)
{
	CGColorMap& colorMap = getColorMap ();
	CGColorMap::const_iterator it = colorMap.find (color);
	if (it != colorMap.end ())
	{
		CGColorRef result = it->second;
		return result;
	}
	const CGFloat components[] = {
		static_cast<CGFloat> (color.red / 255.),
		static_cast<CGFloat> (color.green / 255.),
		static_cast<CGFloat> (color.blue / 255.),
		static_cast<CGFloat> (color.alpha / 255.)
	};
	CGColorRef result = CGColorCreate (GetCGColorSpace (), components);
	colorMap.insert (std::make_pair (color, result));
	return result;
}
void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
{
    state().m_shadowOffset = FloatSize(width, height);
    state().m_shadowBlur = blur;
    state().m_shadowColor = "";

    GraphicsContext* dc = drawingContext();
    if (!dc)
        return;
    // FIXME: Do this through platform-independent GraphicsContext API.
#if PLATFORM(CG)
    const CGFloat components[5] = { c, m, y, k, a };
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
    CGColorRef shadowColor = CGColorCreate(colorSpace, components);
    CGColorSpaceRelease(colorSpace);
    CGContextSetShadowWithColor(dc->platformContext(), CGSizeMake(width, height), blur, shadowColor);
    CGColorRelease(shadowColor);
#endif
}
void wxColour::InitRGBA (ChannelType r, ChannelType g, ChannelType b, ChannelType a)
{
    m_red = r;
    m_green = g;
    m_blue = b;
    m_alpha = a ;

    CGColorRef col = 0 ;
#if wxOSX_USE_COCOA_OR_CARBON && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
    if ( CGColorCreateGenericRGB != NULL )
        col = CGColorCreateGenericRGB( (CGFloat)(r / 255.0), (CGFloat) (g / 255.0), (CGFloat) (b / 255.0), (CGFloat) (a / 255.0) );
    else
#endif
    {
        CGFloat components[4] = { (CGFloat)(r / 255.0), (CGFloat) (g / 255.0), (CGFloat) (b / 255.0), (CGFloat) (a / 255.0) } ;
        col = CGColorCreate( wxMacGetGenericRGBColorSpace() , components ) ;
    }
    m_cgColour.reset( col );
}
void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
{
    state().m_shadowOffset = FloatSize(width, height);
    state().m_shadowBlur = blur;
    state().m_shadowColor = "";

    GraphicsContext* c = drawingContext();
    if (!c)
        return;
    // FIXME: Do this through platform-independent GraphicsContext API.
#if PLATFORM(CG)
    const CGFloat components[2] = { grayLevel, alpha };
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
    CGColorRef color = CGColorCreate(colorSpace, components);
    CGColorSpaceRelease(colorSpace);
    CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
    CGColorRelease(color);
#endif
}
void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
{
    state().m_shadowOffset = FloatSize(width, height);
    state().m_shadowBlur = blur;
    state().m_shadowColor = "";

    GraphicsContext* c = drawingContext();
    if (!c)
        return;
    // FIXME: Do this through platform-independent GraphicsContext API.
#if PLATFORM(CG)
    const CGFloat components[4] = { r, g, b, a };
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGColorRef shadowColor = CGColorCreate(colorSpace, components);
    CGColorSpaceRelease(colorSpace);
    CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor);
    CGColorRelease(shadowColor);
#endif
}
void CanvasRenderingContext2D::applyShadow()
{
    GraphicsContext* c = drawingContext();
    if (!c)
        return;
    // FIXME: Do this through platform-independent GraphicsContext API.
#if PLATFORM(CG)
    RGBA32 rgba = state().m_shadowColor.isEmpty() ? 0 : CSSParser::parseColor(state().m_shadowColor);
    const CGFloat components[4] = {
        ((rgba >> 16) & 0xFF) / 255.0f,
        ((rgba >> 8) & 0xFF) / 255.0f,
        (rgba & 0xFF) / 255.0f,
        ((rgba >> 24) & 0xFF) / 255.0f
    };
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGColorRef color = CGColorCreate(colorSpace, components);
    CGColorSpaceRelease(colorSpace);
    CGContextSetShadowWithColor(c->platformContext(), state().m_shadowOffset, state().m_shadowBlur, color);
    CGColorRelease(color);
#endif
}
void x_async_refresh(CGContextRef myContext,CGRect myBoundingBox)
{
	
#ifdef ENABLEQD
	CEmulatorMac* pEmu = (CEmulatorMac*)CEmulator::theEmulator;
	if (!pEmu) return ;
#endif
    
#ifndef DRIVER_IOS
	x_vbl_count++;
#endif
	
	addFrameRate(0);

	CHANGE_BORDER(1,0xFF);
	
	// OG
	if (macUsingCoreGraphics)
	{
		if(r_sim65816.is_emulator_offscreen_available() && g_kimage_offscreen.dev_handle)
		{
	
            /*
			void addConsoleWindow(Kimage* _dst);
			addConsoleWindow(&g_kimage_offscreen);
	*/
            
			CGContextSaveGState(myContext);
			
#ifndef DRIVER_IOS
		//	CGContextTranslateCTM(myContext,0.0, X_A2_WINDOW_HEIGHT);
        	CGContextTranslateCTM(myContext,0.0, myBoundingBox.size.height);    
			CGContextScaleCTM(myContext,1.0,-1.0);
#endif
			
			
			CGImageRef myImage = CGBitmapContextCreateImage((CGContextRef)g_kimage_offscreen.dev_handle);
            
            
            
			CGContextDrawImage(myContext, myBoundingBox, myImage);// 6
	
#ifndef VIDEO_SINGLEVLINE
			if (r_sim65816.get_video_fx() == VIDEOFX_CRT)
			{
                

				CGContextSetRGBFillColor(myContext,0,0,0,0.5);
				for(int h=0;h<g_kimage_offscreen.height;h+=2)
				{
					CGRect r = CGRectMake(0,h,g_kimage_offscreen.width_act,1);
					CGContextFillRect(myContext,r);
				}
                
			}            
           
#endif
            
			CGImageRelease(myImage);
		
			CGContextRestoreGState(myContext);
#ifndef DRIVER_IOS
			if (!messageLine.IsEmpty())
			{
				CGContextSaveGState(myContext);
				CGContextSetTextMatrix(myContext,CGAffineTransformIdentity);
				CGContextTranslateCTM(myContext,0.0, X_A2_WINDOW_HEIGHT);
				CGContextScaleCTM(myContext,1.0,-1.0);

				CGContextSelectFont(myContext, "Courier", 14.0, kCGEncodingMacRoman);
				CGContextSetTextDrawingMode(myContext, kCGTextFill);
				CGContextSetRGBFillColor (myContext, 1,1, 1, 1);
				CGContextSetShouldAntialias(myContext, true);
#define SHADOW 4.0
                

                CGFloat           myColorValues[] = {0.5, 0.5, 0.5, 1.0};
                
               
                CGColorSpaceRef  myColorSpace = CGColorSpaceCreateDeviceRGB ();// 9
                 CGColorRef  myColor = CGColorCreate (myColorSpace, myColorValues);
				CGContextSetShadowWithColor(myContext, CGSizeMake(SHADOW, -SHADOW), 4,
                                            myColor
                                            //CGColorCreateGenericGray(0.5,1.0)
                    
                                            );
				CGContextShowTextAtPoint(myContext, 20.0, X_A2_WINDOW_HEIGHT-20.0, messageLine.c_str(), messageLine.GetLength());
			
				CGContextRestoreGState(myContext);
				messageLineVBL--;
				if (messageLineVBL<0)
					messageLine.Empty();
				else 
					x_refresh_video();

			}
#endif
			
		}
		else
		{
			CGContextSaveGState(myContext);
#if defined(DRIVER_IOS)
            // efface en noir si l'émulateur n'avait pas encore démarré (le cas sur 3GS)
			CGContextSetRGBFillColor (myContext, 0, 0, 0, 1);
#else
            CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
#endif
			CGContextFillRect (myContext, CGRectMake (0, 0, X_A2_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT));

			CGContextRestoreGState(myContext);

		}
		
	}
	else
	{
#ifdef ENABLEQD
		CGrafPtr window_port = pEmu->window_port;
		Rect src_rect;
		Rect dest_rect;
		SetRect(&src_rect,0,0,704,462);
		SetRect(&dest_rect,0,0,704,462);
		
		if (pixmap_backbuffer)
			CopyBits( (BitMap *)(*pixmap_backbuffer),
					 GetPortBitMapForCopyBits(window_port), &src_rect, &dest_rect,
					 srcCopy, NULL);
		
#endif
	}
	
	
	CHANGE_BORDER(1,0);
    
    if (r_sim65816.is_emulator_offscreen_available() && g_driver.x_handle_state_on_paint)
        g_driver.x_handle_state_on_paint(myBoundingBox.size.width,myBoundingBox.size.height);

}
Beispiel #9
0
static CGColorRef leakCGColor(const Color& color, ColorSpace colorSpace)
{
    CGFloat components[4];
    color.getRGBA(components[0], components[1], components[2], components[3]);
    return CGColorCreate(cachedCGColorSpace(colorSpace), components);
}
void doClippedEllipse(CGContextRef context)
{
    CGPoint theCenterPoint = { 120., 120. };
    CGSize theEllipseSize = { 100., 200. };
    float dash[1] = { 2 };
    static CGColorRef opaqueBrownColor = NULL, opaqueOrangeColor = NULL;
    // Initialize the CGColorRefs if necessary.
    if(opaqueBrownColor == NULL){
		// The initial value of the color array is an 
		// opaque brown in an RGB color space.
		float color[4] = { 0.325, 0.208, 0.157, 1.0 };
		CGColorSpaceRef theColorSpace = getTheCalibratedRGBColorSpace();
		// Create a CGColorRef for opaque brown.
		opaqueBrownColor = CGColorCreate(theColorSpace, color);
		// Make the color array correspond to an opaque orange.
		color[0] = 0.965 ; color[1] = 0.584; color[2] = 0.059;
		// Create another CGColorRef for opaque orange.
		opaqueOrangeColor = CGColorCreate(theColorSpace, color);
	}
	// Draw two ellipses centered about the same point, one
	// rotated 45 degrees from the other.
	CGContextSaveGState(context);
		// Ellipse 1
		createEllipsePath(context, theCenterPoint, theEllipseSize);
		CGContextSetFillColorWithColor(context, opaqueBrownColor);
		CGContextFillPath(context);
		// Translate and rotate about the center point of the ellipse.
		CGContextTranslateCTM(context, theCenterPoint.x, theCenterPoint.y);
		// Rotate by 45 degrees.
		CGContextRotateCTM(context, DEGREES_TO_RADIANS(45));
		// Ellipse 2
		// CGPointZero is a pre-defined Quartz point corresponding to 
		// the coordinate (0,0).
		createEllipsePath(context, CGPointZero, theEllipseSize);
		CGContextSetFillColorWithColor(context, opaqueOrangeColor);
		CGContextFillPath(context);
	CGContextRestoreGState(context);
	
	CGContextTranslateCTM(context, 170., 0.);
	// Now use the first ellipse as a clipping area prior to
	// painting the second ellipse.
	CGContextSaveGState(context);
		// Ellipse 3
		createEllipsePath(context, theCenterPoint, theEllipseSize);
		CGContextSetStrokeColorWithColor(context, opaqueBrownColor);
		CGContextSetLineDash(context, 0, dash, 1);
		// Stroke the path with a dash.
		CGContextStrokePath(context);
		// Ellipse 4
		createEllipsePath(context, theCenterPoint, theEllipseSize);
		// Clip to the elliptical path.
		CGContextClip(context);
		CGContextTranslateCTM(context, theCenterPoint.x, theCenterPoint.y);
		// Rotate by 45 degrees.
		CGContextRotateCTM(context, DEGREES_TO_RADIANS(45));
		// Ellipse 5
		createEllipsePath(context, CGPointZero, theEllipseSize);
		CGContextSetFillColorWithColor(context, opaqueOrangeColor);
		CGContextFillPath(context);
	CGContextRestoreGState(context);
}
Beispiel #11
0
bool FBVLC_Mac::onCoreGraphicsDraw(FB::CoreGraphicsDraw *evt, FB::PluginWindowMacCG*)
{
    boost::lock_guard<boost::mutex> lock( m_frameGuard );

    FB::Rect bounds(evt->bounds);
    //FB::Rect clip(evt->clip);
    short width = bounds.right - bounds.left, height = bounds.bottom - bounds.top;

    CGContextRef cgContext(evt->context);

    CGContextSaveGState(cgContext);

    CGContextTranslateCTM(cgContext, 0.0, height);
    CGContextScaleCTM(cgContext, 1.0, -1.0);

    CGColorSpaceRef cSpace = CGColorSpaceCreateDeviceRGB();
    CGContextSetFillColorSpace(cgContext, cSpace);

    CGColorRef bgColor = CGColorCreate(cSpace, m_bgComponents);
    CGContextSetFillColorWithColor(cgContext, bgColor);

    if ( 0 != m_media_width && 0 != m_media_height ) {
        CGRect imgRect = {
            { (width - m_media_width) / 2, (height - m_media_height) / 2 },
            { m_media_width, m_media_height }
        };

        const std::vector<char>& fb = vlc::vmem::frame_buf();
        CGContextRef frameBmpCtx =
            CGBitmapContextCreate( (void*)&fb[0], m_media_width, m_media_height, 8,
                                   m_media_width * vlc::DEF_PIXEL_BYTES, cSpace,
                                   kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little );
        CGImageRef frameImage = CGBitmapContextCreateImage( frameBmpCtx );

        CGContextDrawImage( cgContext, imgRect, frameImage );

        CGImageRelease( frameImage );
        CGContextRelease( frameBmpCtx );

        if( m_media_width < width ) {
            CGRect bgLeft = {
                { 0, 0 },
                { imgRect.origin.x, height }
            };
            CGContextFillRect(cgContext, bgLeft);

            CGRect bgRight = {
                { imgRect.origin.x + imgRect.size.width, 0 },
                { width - (imgRect.origin.x + imgRect.size.width), height }
            };
            CGContextFillRect(cgContext, bgRight);

        } else if( m_media_height < height ){
            CGRect bgTop = {
                { 0, 0 },
                { width, imgRect.origin.y }
            };
            CGContextFillRect(cgContext, bgTop);

            CGRect bgBottom = {
                { 0, imgRect.origin.y + imgRect.size.height },
                { width, height - (imgRect.origin.y + imgRect.size.height) }
            };
            CGContextFillRect(cgContext, bgBottom);
        }
    } else {
        CGRect cgBounds = {
            { 0, 0 },
            { width, height }
        };
        CGContextFillRect(cgContext, cgBounds);
    }

    CGColorRelease(bgColor);
    CGColorSpaceRelease(cSpace);
    CGContextRestoreGState(cgContext);

    return true; // This is handled
}
Beispiel #12
0
static bool osx_prepare_text(const void *p_text, uindex_t p_length, const MCGFont &p_font)
{
    OSStatus t_err;
	t_err = noErr;
    
	if (t_err == noErr)
		if (s_layout == NULL)
			t_err = ATSUCreateTextLayout(&s_layout);
	
	if (t_err == noErr)
		if (s_style == NULL)
			t_err = ATSUCreateStyle(&s_style);
    
    if (t_err == noErr)
        if (s_colour_space == NULL)
            s_colour_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
    
    if (t_err == noErr)
        if (s_colour == NULL)
        {
            // Components are grey and alpha
            const float t_colour_components[] = {1.0, 1.0};
            s_colour = CGColorCreate(s_colour_space, t_colour_components);
        }
	
	ATSUFontID t_font_id;
	Fixed t_font_size;
	Boolean t_font_is_italic;
	ATSUTextMeasurement t_imposed_width;
    if (t_err == noErr)
    {
        t_font_size = p_font . size << 16;
		
		// MM-2013-09-16: [[ Bug 11283 ]] It appears that ATSUI doesn't like italic being passed as a style parameter to ATSUFONDtoFontID.
		//   Instead, set italic as an attribute tag.
		uint8_t t_style;
		t_style = p_font . style & ~italic;
		t_font_is_italic = p_font . style & italic;
		
		// MW-2013-12-05: [[ Bug 11535 ]] Set the imposed width to the fixed advance width
		//   if non-zero. Otherwise use the glyph advance.
		if (p_font . fixed_advance != 0)
			t_imposed_width = p_font . fixed_advance << 16;
		else
			t_imposed_width = kATSUseGlyphAdvance;
			
		// if the specified font can't be found, just use the default
		// MM-2013-09-16: [[ Bug 11283 ]] Do the same for font styles - if the font/style paring cannot be found, try font with no style.
		t_err = ATSUFONDtoFontID((short)(intptr_t)p_font . fid, t_style, &t_font_id);
		if (t_err != noErr)
			t_err = ATSUFONDtoFontID((short)(intptr_t)p_font . fid, 0, &t_font_id);
		if (t_err != noErr)
			t_err = ATSUFONDtoFontID(0, t_style, &t_font_id);
		if (t_err != noErr)
			t_err = ATSUFONDtoFontID(0, 0, &t_font_id);
    }
	ATSUAttributeTag t_tags[] =
	{
		kATSUFontTag,
		kATSUSizeTag,
		kATSUQDItalicTag,
		kATSUImposeWidthTag,
	};
	ByteCount t_sizes[] =
	{
		sizeof(ATSUFontID),
		sizeof(Fixed),
		sizeof(Boolean),
		sizeof(ATSUTextMeasurement),
	};
	ATSUAttributeValuePtr t_attrs[] =
	{
		&t_font_id,
		&t_font_size,
		&t_font_is_italic,
		&t_imposed_width,
	};
	if (t_err == noErr)
		t_err = ATSUSetAttributes(s_style, sizeof(t_tags) / sizeof(ATSUAttributeTag), t_tags, t_sizes, t_attrs);
	if (t_err == noErr)
		t_err = ATSUSetTextPointerLocation(s_layout, (const UniChar *) p_text, 0, p_length / 2, p_length / 2);
	if (t_err == noErr)
		t_err = ATSUSetRunStyle(s_layout, s_style, 0, p_length / 2);
	if (t_err == noErr)
		t_err = ATSUSetTransientFontMatching(s_layout, true);
	
	return t_err == noErr;
}
void JBGSetUpJumballGraphics()
{
    CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
    
    // 白色
    CGFloat white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
    kJBGWhite = CGColorCreate(color_space, white);
    
    // 浅蓝 204 224 244
    CGFloat light_blue[4] = {204/255.0f, 224/255.0f, 244/255.0f, 1.0f};
    kJBGLightBlue = CGColorCreate(color_space, light_blue);
    
    // 蓝 29 156 215
    CGFloat blue[4] = {29/255.0f, 156/255.0f, 215/255.0f, 1.0f};
    kJBGBlue = CGColorCreate(color_space, blue);
    
    // 深蓝 0 50 126
    CGFloat dark_blue[4] = {0, 50/255.0f, 126/255.0f, 1.0f};
    kJBGDarkBlue = CGColorCreate(color_space, dark_blue);
    
    // 深红 Firebrick3 205 38 38
    CGFloat dark_red[4] = {205/255.0f, 38/255.0f, 38/255.0f, 1.0f};
    kJBGDarkRed = CGColorCreate(color_space, dark_red);
    
    // 红 Firebrick2 238 44 44
    CGFloat red[4] = {238/255.0f, 44/255.0f, 44/255.0f, 1.0f};
    kJBGRed = CGColorCreate(color_space, red);
    
    // 深金 DarkGoldenrod3 205 149 12
    CGFloat dark_gold[4] = {205/255.0f, 149/255.0f, 12/255.0f, 1.0f};
    kJBGDarkGold = CGColorCreate(color_space, dark_gold);
    
    // 金 DarkGoldenrod2	238 173 14 (原来选的是255 215 0)
    CGFloat gold[4] = {238/255.0f, 173.0f/255.0f, 14/255.0f, 1.0f};
    kJBGGold = CGColorCreate(color_space, gold);
    
    // 深绿 SpringGreen3 0 205 102
    CGFloat dark_green[4] = {0, 205/255.0f, 102/255.0f, 1.0f};
    kJBGDarkGreen = CGColorCreate(color_space, dark_green);
    
    // 绿 SpringGreen2 0 238 118
    CGFloat green[4] = {0, 238/255.0f, 118/255.0f, 1.0f};
    kJBGGreen = CGColorCreate(color_space, green);
    
    // 深紫  Purple3 125 38 205
    CGFloat dark_purple[4] = {125/255.0f, 38/255.0f, 205/255.0f, 1.0f};
    kJBGDarkPurple = CGColorCreate(color_space, dark_purple);
    
    // 紫 Purple2 145 44 238
    CGFloat purple[4] = {145/255.0f, 44/255.0f, 238/255.0f, 1.0f};
    kJBGPurple = CGColorCreate(color_space, purple);
    
    // 深粉红 Magenta3	205 0 205
    CGFloat dark_pink[4] = {205/255.0f, 0/255.0f, 205/255.0f, 1.0f};
    kJBGDarkPink = CGColorCreate(color_space, dark_pink);
    
    // 粉红 Magenta2	238 0 238
    CGFloat pink[4] = {238/255.0f, 0/255.0f, 238/255.0f, 1.0f};
    kJBGPink = CGColorCreate(color_space, pink);
    
    // 深黄 Yellow3	205 205 0
    CGFloat dark_yellow[4] = {205, 205/255.0f, 0/255.0f, 1.0f};
    kJBGDarkYellow = CGColorCreate(color_space, dark_yellow);
    
    // 黄 Yellow2	238 238 0
    CGFloat yellow[4] = {238, 238/255.0f, 0/255.0f, 1.0f};
    kJBGYellow = CGColorCreate(color_space, yellow);
    
    CGColorSpaceRelease(color_space);
}
Beispiel #14
0
Memsubfont*
mksubfont(XFont *f, char *name, int lo, int hi, int size, int antialias)
{
	CFStringRef s;
	CGColorSpaceRef color;
	CGContextRef ctxt;
	CTFontRef font;
	CTFontDescriptorRef desc;
	CGRect bbox;
	Memimage *m, *mc, *m1;
	int x, y, y0;
	int i, height, ascent;
	Fontchar *fc, *fc0;
	Memsubfont *sf;
	CGFloat whitef[] = { 1.0, 1.0 };
	CGColorRef white;

	s = c2mac(name);
	desc = CTFontDescriptorCreateWithNameAndSize(s, size);
	CFRelease(s);
	if(desc == nil)
		return nil;
	font = CTFontCreateWithFontDescriptor(desc, 0, nil);
	CFRelease(desc);
	if(font == nil)
		return nil;
	
	
	bbox = CTFontGetBoundingBox(font);
	x = (int)(bbox.size.width*2 + 0.99999999);

	fontheight(f, size, &height, &ascent);
	y = height;
	y0 = height - ascent;

	m = allocmemimage(Rect(0, 0, x*(hi+1-lo)+1, y+1), GREY8);
	if(m == nil)
		return nil;
	mc = allocmemimage(Rect(0, 0, x+1, y+1), GREY8);
	if(mc == nil){
		freememimage(m);
		return nil;
	}
	memfillcolor(m, DBlack);
	memfillcolor(mc, DBlack);
	fc = malloc((hi+2 - lo) * sizeof fc[0]);
	sf = malloc(sizeof *sf);
	if(fc == nil || sf == nil) {
		freememimage(m);
		freememimage(mc);
		free(fc);
		free(sf);
		return nil;
	}
	fc0 = fc;

	color = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
	ctxt = CGBitmapContextCreate(byteaddr(mc, mc->r.min), Dx(mc->r), Dy(mc->r), 8,
		mc->width*sizeof(u32int), color, kCGImageAlphaNone);
	white = CGColorCreate(color, whitef);
	CGColorSpaceRelease(color);
	if(ctxt == nil) {
		freememimage(m);
		freememimage(mc);
		free(fc);
		free(sf);
		return nil;
	}

	CGContextSetAllowsAntialiasing(ctxt, antialias);
	CGContextSetTextPosition(ctxt, 0, 0);	// XXX
#if OSX_VERSION >= 101400
	CGContextSetAllowsFontSmoothing(ctxt, false);
#endif

	x = 0;
	for(i=lo; i<=hi; i++, fc++) {
		char buf[20];
		CFStringRef str;
		CFDictionaryRef attrs;
		CFAttributedStringRef attrString;
		CTLineRef line;
		CGRect r;
		CGPoint p1;
		CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName };
		CFTypeRef values[] = { font, white };

		sprint(buf, "%C", (Rune)mapUnicode(name, i));
 		str = c2mac(buf);
 		
 		// See https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW2
 		attrs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
			(const void**)&values, sizeof(keys) / sizeof(keys[0]),
			&kCFTypeDictionaryKeyCallBacks,
			&kCFTypeDictionaryValueCallBacks);
		attrString = CFAttributedStringCreate(kCFAllocatorDefault, str, attrs);
		CFRelease(str);
		CFRelease(attrs);

		line = CTLineCreateWithAttributedString(attrString);
		CGContextSetTextPosition(ctxt, 0, y0);
		r = CTLineGetImageBounds(line, ctxt);
		memfillcolor(mc, DBlack);
		CTLineDraw(line, ctxt);		
		CFRelease(line);

		fc->x = x;
		fc->top = 0;
		fc->bottom = Dy(m->r);

//		fprint(2, "printed %#x: %g %g\n", mapUnicode(i), p1.x, p1.y);
		p1 = CGContextGetTextPosition(ctxt);
		if(p1.x <= 0 || mapUnicode(name, i) == 0xfffd) {
			fc->width = 0;
			fc->left = 0;
			if(i == 0) {
				drawpjw(m, fc, x, (int)(bbox.size.width + 0.99999999), y, y - y0);
				x += fc->width;
			}	
			continue;
		}

		memimagedraw(m, Rect(x, 0, x + p1.x, y), mc, ZP, memopaque, ZP, S);
		fc->width = p1.x;
		fc->left = 0;
		x += p1.x;
	}
	fc->x = x;

	// round up to 32-bit boundary
	// so that in-memory data is same
	// layout as in-file data.
	if(x == 0)
		x = 1;
	if(y == 0)
		y = 1;
	if(antialias)
		x += -x & 3;
	else
		x += -x & 31;
	m1 = allocmemimage(Rect(0, 0, x, y), antialias ? GREY8 : GREY1);
	memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S);
	freememimage(m);
	freememimage(mc);

	sf->name = nil;
	sf->n = hi+1 - lo;
	sf->height = Dy(m1->r);
	sf->ascent = Dy(m1->r) - y0;
	sf->info = fc0;
	sf->bits = m1;
	
	return sf;
}