Merge pull request #110 from tesselode/fixes
[wdl/wdl-ol.git] / WDL / swell / swell-gdi.mm
blob97c2ce212572480bcb45f1c12ad6c7e78a3a933a
1 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
2    Copyright (C) 2006 and later, Cockos, Inc.
4     This software is provided 'as-is', without any express or implied
5     warranty.  In no event will the authors be held liable for any damages
6     arising from the use of this software.
8     Permission is granted to anyone to use this software for any purpose,
9     including commercial applications, and to alter it and redistribute it
10     freely, subject to the following restrictions:
12     1. The origin of this software must not be misrepresented; you must not
13        claim that you wrote the original software. If you use this software
14        in a product, an acknowledgment in the product documentation would be
15        appreciated but is not required.
16     2. Altered source versions must be plainly marked as such, and must not be
17        misrepresented as being the original software.
18     3. This notice may not be removed or altered from any source distribution.
19   
21     This file provides basic win32 GDI-->Quartz translation. It uses features that require OS X 10.4+
25 #ifndef SWELL_PROVIDED_BY_APP
27 #import <Carbon/Carbon.h>
28 #import <Cocoa/Cocoa.h>
29 #import <CoreFoundation/CFDictionary.h>
30 #import <objc/objc-runtime.h>
31 #include "swell.h"
32 #include "swell-internal.h"
34 #include "../mutex.h"
35 #include "../assocarray.h"
36 #include "../wdlcstring.h"
38 #ifdef __SSE__
39 #include <xmmintrin.h>
40 #endif
42 #ifdef SWELL_SUPPORT_OPENGL_BLIT
43 #include <OpenGL/gl.h>
44 #endif
46 // reimplement here so that swell-gdi isn't dependent on swell-misc, and vice-versa
47 static int SWELL_GDI_GetOSXVersion()
49   static SInt32 v;
50   if (!v)
51   {
52     if (NSAppKitVersionNumber >= 1266.0) 
53     {
54       v=0x10a0; // 10.10+ Gestalt(gsv) return 0x109x, so we bump this to 0x10a0
55     }
56     else 
57     {
58       SInt32 a = 0x1040;
59       Gestalt(gestaltSystemVersion,&a);
60       v=a;
61     }
62   }
63   return v;
66 #ifdef __AVX__
67 #include <immintrin.h>
68 #endif
70 #ifndef MAC_OS_X_VERSION_10_6
71 // 10.5 SDK doesn't include CGContextSetAllowsFontSmoothing() in header (but apparently does in libs)
72 CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef c, bool) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER;
73 #endif
75 #ifndef SWELL_NO_CORETEXT
76 static bool IsCoreTextSupported()
78 #ifdef SWELL_ATSUI_TEXT_SUPPORT
79   return SWELL_GDI_GetOSXVersion() >= 0x1050 && CTFontCreateWithName && CTLineDraw && CTFramesetterCreateWithAttributedString && CTFramesetterCreateFrame && 
80          CTFrameGetLines && CTLineGetTypographicBounds && CTLineCreateWithAttributedString && CTFontCopyPostScriptName
81          ;
82 #else
83   // no ATSUI, targetting 10.5+, CT is always valid
84   return true;
85 #endif
88 static CTFontRef GetCoreTextDefaultFont()
90   static CTFontRef deffr;
91   static bool ok;
92   if (!ok)
93   {
94     ok=true;
95     if (IsCoreTextSupported())
96     {
97       deffr=(CTFontRef) [[NSFont labelFontOfSize:10.0] retain]; 
98     }
99   }
100   return deffr;
102 #endif // !SWELL_NO_CORETEXT
103   
105 static NSString *CStringToNSString(const char *str)
107   if (!str) str="";
108   NSString *ret;
109   
110   ret=(NSString *)CFStringCreateWithCString(NULL,str,kCFStringEncodingUTF8);
111   if (ret) return ret;
112   ret=(NSString *)CFStringCreateWithCString(NULL,str,kCFStringEncodingASCII);
113   return ret;
115 CGColorSpaceRef __GetBitmapColorSpace()
117   static CGColorSpaceRef cs;
118   if (!cs) cs = CGColorSpaceCreateDeviceRGB();
119   return cs;
122 CGColorSpaceRef __GetDisplayColorSpace()
124   static CGColorSpaceRef cs;
125   if (!cs)
126   {
127     // use monitor profile for 10.7+
128     if (SWELL_GDI_GetOSXVersion() >= 0x1070)
129     {
131 #ifdef MAC_OS_X_VERSION_10_11
132       // OSX 10.11 SDK removes CMGetSystemProfile
133       // this may be preferable on older SDKs as well, need to test (though CGDisplayCopyColorSpace is only available on 10.5+)
134       cs = CGDisplayCopyColorSpace(CGMainDisplayID());
135 #else
136       CMProfileRef systemMonitorProfile = NULL;
137       CMError getProfileErr = CMGetSystemProfile(&systemMonitorProfile);
138       if(noErr == getProfileErr)
139       {
140         cs = CGColorSpaceCreateWithPlatformColorSpace(systemMonitorProfile);
141         CMCloseProfile(systemMonitorProfile);
142       }
143 #endif
144     }
145   }
146   if (!cs) 
147     cs = CGColorSpaceCreateDeviceRGB();
148   return cs;
151 static CGColorRef CreateColor(int col, float alpha=1.0f)
153   CGFloat cols[4]={GetRValue(col)/255.0f,GetGValue(col)/255.0f,GetBValue(col)/255.0f,alpha};
154   CGColorRef color=CGColorCreate(__GetBitmapColorSpace(),cols);
155   return color;
159 #include "swell-gdi-internalpool.h"
161 int SWELL_IsRetinaHWND(HWND hwnd)
163   if (!hwnd || SWELL_GDI_GetOSXVersion() < 0x1070) return 0;
165   NSWindow *w=NULL;
166   if ([(id)hwnd isKindOfClass:[NSView class]]) w = [(NSView *)hwnd window];
167   else if ([(id)hwnd isKindOfClass:[NSWindow class]]) w = (NSWindow *)hwnd;
169   if (w)
170   {
171     NSRect r=NSMakeRect(0,0,1,1);
172     NSRect (*tmp)(id receiver, SEL operation, NSRect) = (NSRect (*)(id, SEL, NSRect))objc_msgSend_stret;
173     NSRect str = tmp(w,sel_getUid("convertRectToBacking:"),r);
175     if (str.size.width > 1.9) return 1;
176   }
177   return 0;
180 int SWELL_IsRetinaDC(HDC hdc)
182   HDC__ *src=(HDC__*)hdc;
183   if (!src || !HDC_VALID(src) || !src->ctx) return 0;
184   return CGContextConvertSizeToDeviceSpace((CGContextRef)src->ctx, CGSizeMake(1,1)).width > 1.9 ? 1 : 0;
188 HDC SWELL_CreateGfxContext(void *c)
190   HDC__ *ctx=SWELL_GDP_CTX_NEW();
191   NSGraphicsContext *nsc = (NSGraphicsContext *)c;
192 //  if (![nsc isFlipped])
193 //    nsc = [NSGraphicsContext graphicsContextWithGraphicsPort:[nsc graphicsPort] flipped:YES];
195   ctx->ctx=(CGContextRef)[nsc graphicsPort];
196 //  CGAffineTransform f={1,0,0,-1,0,0};
197   //CGContextSetTextMatrix(ctx->ctx,f);
198   //SetTextColor(ctx,0);
199   
200  // CGContextSelectFont(ctx->ctx,"Arial",12.0,kCGEncodingMacRoman);
201   return ctx;
204 #define ALIGN_EXTRA 63
205 static void *ALIGN_FBUF(void *inbuf)
207   const UINT_PTR extra = ALIGN_EXTRA;
208   return (void *) (((UINT_PTR)inbuf+extra)&~extra); 
211 HDC SWELL_CreateMemContext(HDC hdc, int w, int h)
213   void *buf=calloc(w*4*h+ALIGN_EXTRA,1);
214   if (!buf) return 0;
215   CGContextRef c=CGBitmapContextCreate(ALIGN_FBUF(buf),w,h,8,w*4, __GetBitmapColorSpace(), kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host);
216   if (!c)
217   {
218     free(buf);
219     return 0;
220   }
223   CGContextTranslateCTM(c,0.0,h);
224   CGContextScaleCTM(c,1.0,-1.0);
225   CGContextSetAllowsFontSmoothing(c,0); // we may wish to enable this for some contexts eventually, but this is to match previous behavior
227   HDC__ *ctx=SWELL_GDP_CTX_NEW();
228   ctx->ctx=(CGContextRef)c;
229   ctx->ownedData=buf;
230   // CGContextSelectFont(ctx->ctx,"Arial",12.0,kCGEncodingMacRoman);
231   
232   SetTextColor(ctx,0);
233   return ctx;
236 void SWELL_DeleteGfxContext(HDC ctx)
238   HDC__ *ct=(HDC__ *)ctx;
239   if (HDC_VALID(ct))
240   {   
241     if (ct->ownedData)
242     {
243       CGContextRelease(ct->ctx);
244       free(ct->ownedData);
245     }
246     if (ct->curtextcol) CFRelease(ct->curtextcol); 
247     SWELL_GDP_CTX_DELETE(ct);
248   }
250 HPEN CreatePen(int attr, int wid, int col)
252   return CreatePenAlpha(attr,wid,col,1.0f);
255 HBRUSH CreateSolidBrush(int col)
257   return CreateSolidBrushAlpha(col,1.0f);
262 HPEN CreatePenAlpha(int attr, int wid, int col, float alpha)
264   HGDIOBJ__ *pen=GDP_OBJECT_NEW();
265   pen->type=TYPE_PEN;
266   pen->wid=wid<0?0:wid;
267   pen->color=CreateColor(col,alpha);
268   return pen;
270 HBRUSH  CreateSolidBrushAlpha(int col, float alpha)
272   HGDIOBJ__ *brush=GDP_OBJECT_NEW();
273   brush->type=TYPE_BRUSH;
274   brush->color=CreateColor(col,alpha);
275   brush->wid=0; 
276   return brush;
280 HFONT CreateFontIndirect(LOGFONT *lf)
282   return CreateFont(lf->lfHeight, lf->lfWidth,lf->lfEscapement, lf->lfOrientation, lf->lfWeight, lf->lfItalic, 
283                     lf->lfUnderline, lf->lfStrikeOut, lf->lfCharSet, lf->lfOutPrecision,lf->lfClipPrecision, 
284                     lf->lfQuality, lf->lfPitchAndFamily, lf->lfFaceName);
287 static HGDIOBJ__ global_objs[2];
289 void DeleteObject(HGDIOBJ pen)
291   HGDIOBJ__ *p=(HGDIOBJ__ *)pen;
292   if (p >= global_objs && p < global_objs + sizeof(global_objs)/sizeof(global_objs[0])) return;
294   if (HGDIOBJ_VALID(p))
295   {
296     if (--p->additional_refcnt < 0)
297     {
298       if (p->type == TYPE_PEN || p->type == TYPE_BRUSH || p->type == TYPE_FONT || p->type == TYPE_BITMAP)
299       {
300         if (p->type == TYPE_PEN || p->type == TYPE_BRUSH)
301           if (p->wid<0) return;
302         if (p->color) CGColorRelease(p->color);
304         if (p->ct_FontRef) CFRelease(p->ct_FontRef);
306 #ifdef SWELL_ATSUI_TEXT_SUPPORT
307         if (p->atsui_font_style) ATSUDisposeStyle(p->atsui_font_style);
308 #endif
310         if (p->wid && p->bitmapptr) [p->bitmapptr release]; 
311         GDP_OBJECT_DELETE(p);
312       }
313       // JF> don't free unknown objects, this shouldn't ever happen anyway: else free(p);
314     }
315   }
319 HGDIOBJ SelectObject(HDC ctx, HGDIOBJ pen)
321   HDC__ *c=(HDC__ *)ctx;
322   HGDIOBJ__ *p=(HGDIOBJ__*) pen;
323   HGDIOBJ__ **mod=0;
324   if (!HDC_VALID(c)) return 0;
325   
326   if (p == (HGDIOBJ__*)TYPE_PEN) mod=&c->curpen;
327   else if (p == (HGDIOBJ__*)TYPE_BRUSH) mod=&c->curbrush;
328   else if (p == (HGDIOBJ__*)TYPE_FONT) mod=&c->curfont;
330   if (mod) // clearing a particular thing
331   {
332     HGDIOBJ__ *np=*mod;
333     *mod=0;
334     return HGDIOBJ_VALID(np,(int)(INT_PTR)p)?np:p;
335   }
337   if (!HGDIOBJ_VALID(p)) return 0;
338   
339   if (p->type == TYPE_PEN) mod=&c->curpen;
340   else if (p->type == TYPE_BRUSH) mod=&c->curbrush;
341   else if (p->type == TYPE_FONT) mod=&c->curfont;
342   
343   if (!mod) return 0;
344   
345   HGDIOBJ__ *op=*mod;
346   if (!HGDIOBJ_VALID(op,p->type)) op=(HGDIOBJ__*)(INT_PTR)p->type;
347   if (op != p) *mod=p;  
348   return op;
353 void SWELL_FillRect(HDC ctx, const RECT *r, HBRUSH br)
355   HDC__ *c=(HDC__ *)ctx;
356   HGDIOBJ__ *b=(HGDIOBJ__*) br;
357   if (!HDC_VALID(c) || !HGDIOBJ_VALID(b,TYPE_BRUSH) || b == (HGDIOBJ__*)TYPE_BRUSH || b->type != TYPE_BRUSH) return;
359   if (b->wid<0) return;
360   
361   CGRect rect=CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top);
362   CGContextSetFillColorWithColor(c->ctx,b->color);
363   CGContextFillRect(c->ctx,rect);       
367 void RoundRect(HDC ctx, int x, int y, int x2, int y2, int xrnd, int yrnd)
369         xrnd/=3;
370         yrnd/=3;
371         POINT pts[10]={ // todo: curves between edges
372                 {x,y+yrnd},
373                 {x+xrnd,y},
374                 {x2-xrnd,y},
375                 {x2,y+yrnd},
376                 {x2,y2-yrnd},
377                 {x2-xrnd,y2},
378                 {x+xrnd,y2},
379                 {x,y2-yrnd},            
380     {x,y+yrnd},
381                 {x+xrnd,y},
383         
384         WDL_GDP_Polygon(ctx,pts,sizeof(pts)/sizeof(pts[0]));
387 void Ellipse(HDC ctx, int l, int t, int r, int b)
389   HDC__ *c=(HDC__ *)ctx;
390   if (!HDC_VALID(c)) return;
391   
392   CGRect rect=CGRectMake(l,t,r-l,b-t);
393   
394   if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >=0)
395   {
396     CGContextSetFillColorWithColor(c->ctx,c->curbrush->color);
397     CGContextFillEllipseInRect(c->ctx,rect);    
398   }
399   if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0)
400   {
401     CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
402     CGContextStrokeEllipseInRect(c->ctx, rect); //, (float)wdl_max(1,c->curpen->wid));
403   }
406 void Rectangle(HDC ctx, int l, int t, int r, int b)
408   HDC__ *c=(HDC__ *)ctx;
409   if (!HDC_VALID(c)) return;
410   
411   CGRect rect=CGRectMake(l,t,r-l,b-t);
412   
413   if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0)
414   {
415     CGContextSetFillColorWithColor(c->ctx,c->curbrush->color);
416     CGContextFillRect(c->ctx,rect);     
417   }
418   if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0)
419   {
420     CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
421     CGContextStrokeRectWithWidth(c->ctx, rect, (float)wdl_max(1,c->curpen->wid));
422   }
426 HGDIOBJ GetStockObject(int wh)
428   switch (wh)
429   {
430     case NULL_BRUSH:
431     {
432       HGDIOBJ__ *p = &global_objs[0];
433       p->type=TYPE_BRUSH;
434       p->wid=-1;
435       return p;
436     }
437     case NULL_PEN:
438     {
439       HGDIOBJ__ *p = &global_objs[1];
440       p->type=TYPE_PEN;
441       p->wid=-1;
442       return p;
443     }
444   }
445   return 0;
448 void Polygon(HDC ctx, POINT *pts, int npts)
450   HDC__ *c=(HDC__ *)ctx;
451   if (!HDC_VALID(c)) return;
452   if (((!HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH)||c->curbrush->wid<0) && (!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0)) || npts<2) return;
454   CGContextBeginPath(c->ctx);
455   CGContextMoveToPoint(c->ctx,(float)pts[0].x,(float)pts[0].y);
456   int x;
457   for (x = 1; x < npts; x ++)
458   {
459     CGContextAddLineToPoint(c->ctx,(float)pts[x].x,(float)pts[x].y);
460   }
461   if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0)
462   {
463     CGContextSetFillColorWithColor(c->ctx,c->curbrush->color);
464   }
465   if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0)
466   {
467     CGContextSetLineWidth(c->ctx,(float)wdl_max(c->curpen->wid,1));
468     CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);  
469   }
470   CGContextDrawPath(c->ctx,HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0 && HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid>=0 ?  kCGPathFillStroke : HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0 ? kCGPathStroke : kCGPathFill);
473 void MoveToEx(HDC ctx, int x, int y, POINT *op)
475   HDC__ *c=(HDC__ *)ctx;
476   if (!HDC_VALID(c)) return;
477   if (op) 
478   { 
479     op->x = (int) (c->lastpos_x);
480     op->y = (int) (c->lastpos_y);
481   }
482   c->lastpos_x=(float)x;
483   c->lastpos_y=(float)y;
486 void PolyBezierTo(HDC ctx, POINT *pts, int np)
488   HDC__ *c=(HDC__ *)ctx;
489   if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||np<3) return;
490   
491   CGContextSetLineWidth(c->ctx,(float)wdl_max(c->curpen->wid,1));
492   CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
493         
494   CGContextBeginPath(c->ctx);
495   CGContextMoveToPoint(c->ctx,c->lastpos_x,c->lastpos_y);
496   int x; 
497   float xp,yp;
498   for (x = 0; x < np-2; x += 3)
499   {
500     CGContextAddCurveToPoint(c->ctx,
501       (float)pts[x].x,(float)pts[x].y,
502       (float)pts[x+1].x,(float)pts[x+1].y,
503       xp=(float)pts[x+2].x,yp=(float)pts[x+2].y);    
504   }
505   c->lastpos_x=(float)xp;
506   c->lastpos_y=(float)yp;
507   CGContextStrokePath(c->ctx);
511 void SWELL_LineTo(HDC ctx, int x, int y)
513   HDC__ *c=(HDC__ *)ctx;
514   if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0) return;
516   float w = (float)wdl_max(c->curpen->wid,1);
517   CGContextSetLineWidth(c->ctx,w);
518   CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
519         
520   CGContextBeginPath(c->ctx);
521   CGContextMoveToPoint(c->ctx,c->lastpos_x + w * 0.5,c->lastpos_y + w*0.5);
522   float fx=(float)x,fy=(float)y;
523   
524   CGContextAddLineToPoint(c->ctx,fx+w*0.5,fy+w*0.5);
525   c->lastpos_x=fx;
526   c->lastpos_y=fy;
527   CGContextStrokePath(c->ctx);
530 void PolyPolyline(HDC ctx, POINT *pts, DWORD *cnts, int nseg)
532   HDC__ *c=(HDC__ *)ctx;
533   if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||nseg<1) return;
535   float w = (float)wdl_max(c->curpen->wid,1);
536   CGContextSetLineWidth(c->ctx,w);
537   CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color);
538         
539   CGContextBeginPath(c->ctx);
540   
541   while (nseg-->0)
542   {
543     DWORD cnt=*cnts++;
544     if (!cnt) continue;
545     if (!--cnt) { pts++; continue; }
546     
547     CGContextMoveToPoint(c->ctx,(float)pts->x+w*0.5,(float)pts->y+w*0.5);
548     pts++;
549     
550     while (cnt--)
551     {
552       CGContextAddLineToPoint(c->ctx,(float)pts->x+w*0.5,(float)pts->y+w*0.5);
553       pts++;
554     }
555   }
556   CGContextStrokePath(c->ctx);
558 void *SWELL_GetCtxGC(HDC ctx)
560   HDC__ *ct=(HDC__ *)ctx;
561   if (!HDC_VALID(ct)) return 0;
562   return ct->ctx;
566 void SWELL_SetPixel(HDC ctx, int x, int y, int c)
568   HDC__ *ct=(HDC__ *)ctx;
569   if (!HDC_VALID(ct)) return;
570   CGContextBeginPath(ct->ctx);
571   CGContextMoveToPoint(ct->ctx,(float)x-0.5,(float)y-0.5);
572   CGContextAddLineToPoint(ct->ctx,(float)x+0.5,(float)y+0.5);
573   CGContextSetLineWidth(ct->ctx,(float)1.0);
574   CGContextSetRGBStrokeColor(ct->ctx,GetRValue(c)/255.0,GetGValue(c)/255.0,GetBValue(c)/255.0,1.0);
575   CGContextStrokePath(ct->ctx); 
579 static WDL_Mutex s_fontnamecache_mutex;
581 #ifdef SWELL_CLEANUP_ON_UNLOAD
582 static void releaseString(NSString *s) { [s release]; }
583 #endif
584 static WDL_StringKeyedArray<NSString *> s_fontnamecache(true,
585 #ifdef SWELL_CLEANUP_ON_UNLOAD
586       releaseString
587 #else
588       NULL
589 #endif
590       );
592 static NSString *SWELL_GetCachedFontName(const char *nm)
594   NSString *ret = NULL;
595   if (nm && *nm)
596   {
597     s_fontnamecache_mutex.Enter();
598     ret = s_fontnamecache.Get(nm);
599     s_fontnamecache_mutex.Leave();
600     if (!ret)
601     {
602       ret = CStringToNSString(nm);
603       if (ret)
604       {
605 #ifndef SWELL_NO_CORETEXT
606         // only do postscript name lookups on 10.9+
607         if (floor(NSFoundationVersionNumber) > 945.00) // NSFoundationVersionNumber10_8
608         {
609           NSFont *font = [NSFont fontWithName:ret size:10];
610           NSString *nr = font ? (NSString *)CTFontCopyPostScriptName((CTFontRef)font) : NULL;
611           if (nr) 
612           {
613             [ret release];
614             ret = nr;
615           }
616         }
617 #endif
619         s_fontnamecache_mutex.Enter();
620         s_fontnamecache.Insert(nm,ret);
621         s_fontnamecache_mutex.Leave();
622       }
623     }
624   }
625   return ret ? ret : @"";
628 HFONT CreateFont(int lfHeight, int lfWidth, int lfEscapement, int lfOrientation, int lfWeight, char lfItalic, 
629                  char lfUnderline, char lfStrikeOut, char lfCharSet, char lfOutPrecision, char lfClipPrecision, 
630                  char lfQuality, char lfPitchAndFamily, const char *lfFaceName)
632   HGDIOBJ__ *font=GDP_OBJECT_NEW();
633   font->type=TYPE_FONT;
634   float fontwid=lfHeight;
635   
636   if (!fontwid) fontwid=lfWidth;
637   if (fontwid<0)fontwid=-fontwid;
638   
639   if (fontwid < 2 || fontwid > 8192) fontwid=10;
640   
641   font->font_rotation = lfOrientation/10.0;
643 #ifndef SWELL_NO_CORETEXT
644   if (IsCoreTextSupported())
645   {
646     char buf[1024];
647     lstrcpyn_safe(buf,lfFaceName,900);
648     if (lfWeight >= FW_BOLD) strcat(buf," Bold");
649     if (lfItalic) strcat(buf," Italic");
651     font->ct_FontRef = (void*)CTFontCreateWithName((CFStringRef)SWELL_GetCachedFontName(buf),fontwid,NULL);
652     if (!font->ct_FontRef) font->ct_FontRef = (void*)[[NSFont labelFontOfSize:fontwid] retain]; 
654     font->font_quality = (!lfQuality || lfQuality == ANTIALIASED_QUALITY || lfQuality == NONANTIALIASED_QUALITY ? lfQuality : 0);
656     // might want to make this conditional (i.e. only return font if created successfully), but I think we'd rather fallback to a system font than use ATSUI
657     return font;
658   }
659 #endif
660   
661 #ifdef SWELL_ATSUI_TEXT_SUPPORT
662   ATSUFontID fontid=kATSUInvalidFontID;
663   if (lfFaceName && lfFaceName[0])
664   {
665     ATSUFindFontFromName(lfFaceName,strlen(lfFaceName),kFontFullName /* kFontFamilyName? */ ,(FontPlatformCode)kFontNoPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontid);
666     //    if (fontid==kATSUInvalidFontID) printf("looked up %s and got %d\n",lfFaceName,fontid);
667   }
668   
669   if (ATSUCreateStyle(&font->atsui_font_style) == noErr && font->atsui_font_style)
670   {    
671     Fixed fsize=Long2Fix(fontwid);
672     
673     Boolean isBold=lfWeight >= FW_BOLD;
674     Boolean isItal=!!lfItalic;
675     Boolean isUnder=!!lfUnderline;
676     
677     ATSStyleRenderingOptions render;
678     if (!lfQuality)
679       render = kATSStyleNoOptions;
680     else if (lfQuality == ANTIALIASED_QUALITY)
681       render = kATSStyleApplyAntiAliasing;
682     else if (lfQuality == NONANTIALIASED_QUALITY)
683       render = kATSStyleNoAntiAliasing;
684     else
685       render = kATSStyleNoOptions;
687     ATSUAttributeTag        theTags[] = { kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag,kATSUSizeTag,kATSUFontTag, kATSUStyleRenderingOptionsTag };
688     ByteCount               theSizes[] = { sizeof(Boolean),sizeof(Boolean),sizeof(Boolean), sizeof(Fixed),sizeof(ATSUFontID), sizeof(ATSStyleRenderingOptions)  };
689     ATSUAttributeValuePtr   theValues[] =  {&isBold, &isItal, &isUnder,  &fsize, &fontid, &render } ;
690     
691     int attrcnt=sizeof(theTags)/sizeof(theTags[0]);
692     if (fontid == kATSUInvalidFontID) attrcnt--;    
693     
694     if (ATSUSetAttributes (font->atsui_font_style,                       
695                        attrcnt,                       
696                        theTags,                      
697                        theSizes,                      
698                        theValues)!=noErr)
699     {
700       ATSUDisposeStyle(font->atsui_font_style);
701       font->atsui_font_style=0;
702     }
703   }
704   else
705     font->atsui_font_style=0;
706   
707 #endif
708   
709   
710   return font;
713 int GetTextFace(HDC ctx, int nCount, LPTSTR lpFaceName)
715   HDC__ *ct=(HDC__*)ctx;
716   if (!HDC_VALID(ct) || !nCount || !lpFaceName) return 0;
717   
718 #ifndef SWELL_NO_CORETEXT
719   CTFontRef fr=NULL;
720   if (HGDIOBJ_VALID(ct->curfont,TYPE_FONT)) fr=(CTFontRef)ct->curfont->ct_FontRef;
721   if (!fr)  fr=GetCoreTextDefaultFont();
722   
723   if (fr)
724   {
725     CFStringRef name=CTFontCopyDisplayName(fr);
726     const char* p=[(NSString*)name UTF8String];
727     if (p)
728     {
729       lstrcpyn_safe(lpFaceName, p, nCount);
730       return (int)strlen(lpFaceName);
731     }
732   }
733 #endif
734   
735   return 0;
738 BOOL GetTextMetrics(HDC ctx, TEXTMETRIC *tm)
740   HDC__ *ct=(HDC__ *)ctx;
741   if (tm) // give some sane defaults
742   {
743     tm->tmInternalLeading=3;
744     tm->tmAscent=12;
745     tm->tmDescent=4;
746     tm->tmHeight=16;
747     tm->tmAveCharWidth = 10;
748   }
749   if (!HDC_VALID(ct)||!tm) return 0;
751   bool curfont_valid=HGDIOBJ_VALID(ct->curfont,TYPE_FONT);
753 #ifdef SWELL_ATSUI_TEXT_SUPPORT
754   if (curfont_valid && ct->curfont->atsui_font_style)
755   {
756     ATSUTextMeasurement ascent=Long2Fix(10);
757     ATSUTextMeasurement descent=Long2Fix(3);
758     ATSUTextMeasurement sz=Long2Fix(0);
759     ATSUTextMeasurement width =Long2Fix(12);
760     ATSUGetAttribute(ct->curfont->atsui_font_style,  kATSUAscentTag, sizeof(ATSUTextMeasurement), &ascent,NULL);
761     ATSUGetAttribute(ct->curfont->atsui_font_style,  kATSUDescentTag, sizeof(ATSUTextMeasurement), &descent,NULL);
762     ATSUGetAttribute(ct->curfont->atsui_font_style,  kATSUSizeTag, sizeof(ATSUTextMeasurement), &sz,NULL);
763     ATSUGetAttribute(ct->curfont->atsui_font_style, kATSULineWidthTag, sizeof(ATSUTextMeasurement),&width,NULL);
764     
765     float asc=Fix2X(ascent);
766     float desc=Fix2X(descent);
767     float size = Fix2X(sz);
768     
769     if (size < (asc+desc)*0.2) size=asc+desc;
770             
771     tm->tmAscent = (int)ceil(asc);
772     tm->tmDescent = (int)ceil(desc);
773     tm->tmInternalLeading=(int)ceil(asc+desc-size);
774     if (tm->tmInternalLeading<0)tm->tmInternalLeading=0;
775     tm->tmHeight=(int) ceil(asc+desc);
776     tm->tmAveCharWidth = (int) (ceil(asc+desc)*0.65); // (int)ceil(Fix2X(width));
777     
778     return 1;
779   }
780 #endif
782 #ifndef SWELL_NO_CORETEXT
783   CTFontRef fr = curfont_valid ? (CTFontRef)ct->curfont->ct_FontRef : NULL;
784   if (!fr)  fr=GetCoreTextDefaultFont();
786   if (fr)
787   {
788     tm->tmInternalLeading = CTFontGetLeading(fr);
789     tm->tmAscent = CTFontGetAscent(fr);
790     tm->tmDescent = CTFontGetDescent(fr);
791     tm->tmHeight = (tm->tmInternalLeading + tm->tmAscent + tm->tmDescent);
792     tm->tmAveCharWidth = tm->tmHeight*2/3; // todo
794     if (tm->tmHeight)  tm->tmHeight++;
795     
796     return 1;
797   }
798 #endif
800   
801   return 1;
806 #ifdef SWELL_ATSUI_TEXT_SUPPORT
808 static int DrawTextATSUI(HDC ctx, CFStringRef strin, RECT *r, int align, bool *err)
810   HDC__ *ct=(HDC__ *)ctx;
811   HGDIOBJ__ *font=ct->curfont; // caller must specify a valid font
812   
813   UniChar strbuf[4096];
814   int strbuf_len;
816   {
817     strbuf[0]=0;
818     CFRange r = {0,CFStringGetLength(strin)};
819     if (r.length > 4095) r.length=4095;
820     strbuf_len=CFStringGetBytes(strin,r,kCFStringEncodingUTF16,' ',false,(UInt8*)strbuf,sizeof(strbuf)-2,NULL);
821     if (strbuf_len<0)strbuf_len=0;
822     else if (strbuf_len>4095) strbuf_len=4095;
823     strbuf[strbuf_len]=0;
824   }
825   
826   {
827     ATSUAttributeTag        theTags[] = { kATSUColorTag,   };
828     ByteCount               theSizes[] = { sizeof(RGBColor),  };
830     RGBColor tcolor;
831     ATSUAttributeValuePtr   theValues[] =  {&tcolor,  } ;
832     
833     tcolor.red = GetRValue(ct->cur_text_color_int)*256;
834     tcolor.green = GetGValue(ct->cur_text_color_int)*256;
835     tcolor.blue = GetBValue(ct->cur_text_color_int)*256;
836     
837     // error check this? we can live with the wrong color maybe?
838     ATSUSetAttributes(font->atsui_font_style,  sizeof(theTags)/sizeof(theTags[0]), theTags, theSizes, theValues);
839   }
840   
841   UniCharCount runLengths[1]={kATSUToTextEnd};
842   ATSUTextLayout layout; 
843   if (ATSUCreateTextLayoutWithTextPtr(strbuf, kATSUFromTextBeginning, kATSUToTextEnd, strbuf_len, 1, runLengths, &font->atsui_font_style, &layout)!=noErr)
844   {
845     *err=true;
846     return 0;
847   }
848   
849   {
850     Fixed frot = X2Fix(font->font_rotation);
851     
852     ATSULineTruncation tv = (align & DT_END_ELLIPSIS) ? kATSUTruncateEnd : kATSUTruncateNone;
853     ATSUAttributeTag        theTags[] = { kATSUCGContextTag, kATSULineTruncationTag, kATSULineRotationTag };
854     ByteCount               theSizes[] = { sizeof (CGContextRef), sizeof(ATSULineTruncation), sizeof(Fixed)};
855     ATSUAttributeValuePtr   theValues[] =  { &ct->ctx, &tv, &frot } ;
856     
857     
858     if (ATSUSetLayoutControls (layout,
859                            
860                            sizeof(theTags)/sizeof(theTags[0]),
861                            
862                            theTags,
863                            
864                            theSizes,
865                            
866                            theValues)!=noErr)
867     {
868       *err=true;
869       ATSUDisposeTextLayout(layout);   
870       return 0;
871     }
872   }
873   
874   
875   ATSUTextMeasurement leftFixed, rightFixed, ascentFixed, descentFixed;
877   if (ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, kATSUToTextEnd, &leftFixed, &rightFixed, &ascentFixed, &descentFixed)!=noErr) 
878   {
879     *err=true;
880     ATSUDisposeTextLayout(layout);   
881     return 0;
882   }
884   int w=Fix2Long(rightFixed);
885   int descent=Fix2Long(descentFixed);
886   int h=descent + Fix2Long(ascentFixed);
887   if (align&DT_CALCRECT)
888   {
889     ATSUDisposeTextLayout(layout);   
890     r->right=r->left+w;
891     r->bottom=r->top+h;
892     return h;  
893   }
894   CGContextSaveGState(ct->ctx);    
896   if (!(align & DT_NOCLIP))
897     CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top));
899   int l=r->left, t=r->top;
900     
901   if (fabs(font->font_rotation)<45.0)
902   {
903     if (align & DT_RIGHT) l = r->right-w;
904     else if (align & DT_CENTER) l = (r->right+r->left)/2 - w/2;      
905   }
906   else l+=Fix2Long(ascentFixed); // 90 degree special case (we should generalize this to be correct throughout the rotation range, but oh well)
907     
908   if (align & DT_BOTTOM) t = r->bottom-h;
909   else if (align & DT_VCENTER) t = (r->bottom+r->top)/2 - h/2;
910     
911   CGContextTranslateCTM(ct->ctx,0,t);
912   CGContextScaleCTM(ct->ctx,1,-1);
913   CGContextTranslateCTM(ct->ctx,0,-t-h);
914     
915   if (ct->curbkmode == OPAQUE)
916   {      
917     CGRect bgr = CGRectMake(l, t, w, h);
918     CGColorRef bgc = CreateColor(ct->curbkcol);
919     CGContextSetFillColorWithColor(ct->ctx, bgc);
920     CGContextFillRect(ct->ctx, bgr);
921     CGColorRelease(bgc);        
922   }
924   if (ATSUDrawText(layout,kATSUFromTextBeginning,kATSUToTextEnd,Long2Fix(l),Long2Fix(t+descent))!=noErr)
925     *err=true;
926   
927   CGContextRestoreGState(ct->ctx);    
928   
929   ATSUDisposeTextLayout(layout);   
930   
931   return h;
934 #endif
936 int DrawText(HDC ctx, const char *buf, int buflen, RECT *r, int align)
938   HDC__ *ct=(HDC__ *)ctx;
939   if (!HDC_VALID(ct)) return 0;
940   
941   bool has_ml=false;
942   char tmp[4096];
943   const char *p=buf;
944   char *op=tmp;
945   while (*p && (op-tmp)<sizeof(tmp)-1 && (buflen<0 || (p-buf)<buflen))
946   {
947     if (*p == '&' && !(align&DT_NOPREFIX)) p++; 
949     if (*p == '\r')  p++; 
950     else if (*p == '\n' && (align&DT_SINGLELINE)) { *op++ = ' '; p++; }
951     else 
952     {
953       if (*p == '\n') has_ml=true;
954       *op++=*p++;
955     }
956   }
957   *op=0;
958   
959   if (!tmp[0]) return 0; // dont draw empty strings
960   
961   NSString *str=CStringToNSString(tmp);
962   
963   if (!str) return 0;
964   
965   bool curfont_valid = HGDIOBJ_VALID(ct->curfont,TYPE_FONT);
966 #ifdef SWELL_ATSUI_TEXT_SUPPORT
967   if (curfont_valid && ct->curfont->atsui_font_style)
968   {
969     bool err=false;
970     int ret =  DrawTextATSUI(ctx,(CFStringRef)str,r,align,&err);
971     [str release];
972     
973     if (!err) return ret;
974     return 0;
975   }
976 #endif  
977   
978 #ifndef SWELL_NO_CORETEXT
979   CTFontRef fr = curfont_valid ? (CTFontRef)ct->curfont->ct_FontRef : NULL;
980   if (!fr)  fr=GetCoreTextDefaultFont();
981   if (fr)
982   {
983     // Initialize string, font, and context
984     CFStringRef keys[] = { kCTFontAttributeName,kCTForegroundColorAttributeName };
985     CFTypeRef values[] = { fr,ct->curtextcol };
986     
987     int nk= sizeof(keys) / sizeof(keys[0]);
988     if (!values[1]) nk--;
989     
990     CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, (const void**)&values, nk,
991                                 &kCFTypeDictionaryKeyCallBacks,
992                                 &kCFTypeDictionaryValueCallBacks);
993        
994     CFAttributedStringRef attrString =
995           CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)str, attributes);
996     CFRelease(attributes);
997     [str release];
1000     CTFrameRef frame = NULL;
1001     CFArrayRef lines = NULL;
1002     CTLineRef line = NULL;
1003     CGFloat asc=0;
1004     int line_w=0,line_h=0;
1005     if (has_ml)
1006     {
1007       CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
1008       if (framesetter)
1009       {
1010         CGMutablePathRef path=CGPathCreateMutable();
1011         CGPathAddRect(path,NULL,CGRectMake(0,0,100000,100000));
1012         frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0),path,NULL);
1013         CFRelease(framesetter);
1014         CFRelease(path);
1015       }
1016       if (frame)
1017       {
1018         lines = CTFrameGetLines(frame);
1019         const int n = (int)CFArrayGetCount(lines);
1020         for (int x=0;x<n;x++)
1021         {
1022           CTLineRef l = (CTLineRef)CFArrayGetValueAtIndex(lines,x);
1023           if (l)
1024           {
1025             CGFloat desc=0,lead=0;
1026             int w = (int) floor(CTLineGetTypographicBounds(l,&asc,&desc,&lead)+0.5);
1027             int h =(int) floor(asc+desc+lead+1.5);
1028             line_h+=h;
1029             if (line_w < w) line_w=w;
1030           }
1031         }
1032       }
1033     }
1034     else
1035     {
1036       line = CTLineCreateWithAttributedString(attrString);
1037        
1038       if (line)
1039       {
1040         CGFloat desc=0,lead=0;
1041         line_w = (int) floor(CTLineGetTypographicBounds(line,&asc,&desc,&lead)+0.5);
1042         line_h =(int) floor(asc+desc+lead+1.5);
1043       }
1044     }
1045     if (line_h) line_h++;
1047     CFRelease(attrString);
1048     
1049     if (align & DT_CALCRECT)
1050     {
1051       r->right = r->left+line_w;
1052       r->bottom = r->top+line_h;
1053       if (line) CFRelease(line);
1054       if (frame) CFRelease(frame);
1055       return line_h;
1056     }
1058     float xo=r->left,yo=r->top;
1059     if (align & DT_RIGHT) xo += (r->right-r->left) - line_w;
1060     else if (align & DT_CENTER) xo += (r->right-r->left)/2 - line_w/2;
1062     if (align & DT_BOTTOM) yo += (r->bottom-r->top) - line_h;
1063     else if (align & DT_VCENTER) yo += (r->bottom-r->top)/2 - line_h/2;
1065     
1066     CGContextSaveGState(ct->ctx);
1068     CGAffineTransform f={1,0,0,-1,0,0};
1069     CGContextSetTextMatrix(ct->ctx, f);
1071     if (!(align & DT_NOCLIP))
1072     {
1073       CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top));          
1074     }
1075     
1076     CGColorRef bgc = NULL;
1077     if (ct->curbkmode == OPAQUE)
1078     {      
1079       bgc = CreateColor(ct->curbkcol);
1080     }
1082     if (ct->curfont->font_quality)
1083     {
1084       CGContextSetShouldAntialias(ct->ctx, ct->curfont->font_quality == ANTIALIASED_QUALITY);
1085     }
1087     if (line) 
1088     {
1089       if (bgc)
1090       {
1091         CGContextSetFillColorWithColor(ct->ctx, bgc);
1092         CGContextFillRect(ct->ctx, CGRectMake(xo,yo,line_w,line_h));
1093       }
1094       CGContextSetTextPosition(ct->ctx, xo, yo + asc);
1095       CTLineDraw(line,ct->ctx);
1097     }
1098     if (lines)
1099     {
1100       const int n = (int)CFArrayGetCount(lines);
1101       for (int x=0;x<n;x++)
1102       {
1103         CTLineRef l = (CTLineRef)CFArrayGetValueAtIndex(lines,x);
1104         if (l)
1105         {
1106           CGFloat desc=0.0,lead=0.0;
1107           asc=0.0;
1108           float lw=CTLineGetTypographicBounds(l,&asc,&desc,&lead);
1110           if (bgc)
1111           {
1112             CGContextSetFillColorWithColor(ct->ctx, bgc);
1113             CGContextFillRect(ct->ctx, CGRectMake(xo,yo,lw,asc+desc+lead));
1114           }
1115           CGContextSetTextPosition(ct->ctx, xo, yo + asc);
1116           CTLineDraw(l,ct->ctx);
1117           
1118           yo += floor(asc+desc+lead+0.5);          
1119         }
1120       }
1121     }
1122     
1123     CGContextRestoreGState(ct->ctx);
1124     if (bgc) CGColorRelease(bgc);
1125     if (line) CFRelease(line);
1126     if (frame) CFRelease(frame);
1127     
1128     return line_h;
1129   }
1130 #endif
1131   
1132   
1133   [str release];
1134   return 0;  
1138 int GetGlyphIndicesW(HDC ctx, wchar_t *buf, int len, unsigned short *indices, int flags)
1140   HDC__ *ct=(HDC__*)ctx;
1141   if (HDC_VALID(ct) && HGDIOBJ_VALID(ct->curfont, TYPE_FONT))
1142   {
1143 #ifndef SWELL_NO_CORETEXT
1144     CTFontRef f=(CTFontRef)ct->curfont->ct_FontRef;
1145     if (f && CTFontGetGlyphsForCharacters(f, (const UniChar*)buf, (CGGlyph*)indices, (CFIndex)len)) return len;
1146 #endif
1147   }
1148   
1149   int i;
1150   for (i=0; i < len; ++i) indices[i]=(flags == GGI_MARK_NONEXISTING_GLYPHS ? 0xFFFF : 0);
1151   return 0;
1157 void SetBkColor(HDC ctx, int col)
1159   HDC__ *ct=(HDC__ *)ctx;
1160   if (!HDC_VALID(ct)) return;
1161   ct->curbkcol=col;
1164 void SetBkMode(HDC ctx, int col)
1166   HDC__ *ct=(HDC__ *)ctx;
1167   if (!HDC_VALID(ct)) return;
1168   ct->curbkmode=col;
1171 int GetTextColor(HDC ctx)
1173   HDC__ *ct=(HDC__ *)ctx;
1174   if (!HDC_VALID(ct)) return -1;
1175   return ct->cur_text_color_int;
1178 void SetTextColor(HDC ctx, int col)
1180   HDC__ *ct=(HDC__ *)ctx;
1181   if (!HDC_VALID(ct)) return;
1182   ct->cur_text_color_int = col;
1183   
1184   if (ct->curtextcol) CFRelease(ct->curtextcol);
1186   ct->curtextcol = CreateColor(col);
1190 HICON CreateIconIndirect(ICONINFO* iconinfo)
1192   if (!iconinfo || !iconinfo->fIcon) return 0;  
1193   HGDIOBJ__* i=iconinfo->hbmColor;
1194   if (!HGDIOBJ_VALID(i,TYPE_BITMAP) || !i->bitmapptr) return 0;
1195   NSImage* img=i->bitmapptr;
1196   if (!img) return 0;
1197     
1198   HGDIOBJ__* icon=GDP_OBJECT_NEW();
1199   icon->type=TYPE_BITMAP;
1200   icon->wid=1;
1201   [img retain];
1202   icon->bitmapptr=img;
1203   return icon;   
1206 HICON LoadNamedImage(const char *name, bool alphaFromMask)
1208   NSImage *img=0;
1209   NSString *str=CStringToNSString(name); 
1210   if (strstr(name,"/"))
1211   {
1212     img=[[NSImage alloc] initWithContentsOfFile:str];
1213   }
1214   if (!img) 
1215   {
1216     img=[NSImage imageNamed:str];
1217     if (img) [img retain];
1218   }
1219   [str release];
1220   if (!img) 
1221   {
1222     return 0;
1223   }
1224     
1225   [img setFlipped:YES];
1226   if (alphaFromMask)
1227   {
1228     const NSSize sz=[img size];
1229     const int w = (int)sz.width, h=(int)sz.height;
1230     HDC hdc;
1231     if (w>0 && h>0 && NULL != (hdc=SWELL_CreateMemContext(NULL,w,h)))
1232     {
1233       [NSGraphicsContext saveGraphicsState];
1234       NSGraphicsContext *gc=[NSGraphicsContext graphicsContextWithGraphicsPort:((struct HDC__*)hdc)->ctx flipped:NO];
1235       [NSGraphicsContext setCurrentContext:gc];
1236       [img drawInRect:NSMakeRect(0,0,w,h) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
1237       [NSGraphicsContext restoreGraphicsState];
1239       // on yosemite, calling [img TIFFRepresentation] seems to change img somehow for some images, ouch.
1240       // in this case, we should always replace img with newImage (set rcnt=1), but in general
1241       // maybe we shoulnt use alphaFromMask anyhow
1242       NSImage *newImage=[[NSImage alloc] initWithData:[img TIFFRepresentation]];
1243       [newImage setFlipped:YES];
1245       const int *fb = (const int *)SWELL_GetCtxFrameBuffer(hdc);
1246       int y,rcnt=0;
1247       [newImage lockFocus];
1248       CGContextRef myContext = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
1249       for (y=0; y < h; y ++)
1250       {
1251         int x;
1252         for (x = 0; x < w; x++)
1253         {
1254           if ((*fb++ & 0xffffff) == 0xff00ff)
1255           {
1256             CGContextClearRect(myContext,CGRectMake(x,y,1,1));
1257             rcnt++;
1258           }
1259         }
1260       }
1261       [newImage unlockFocus];
1263       SWELL_DeleteGfxContext(hdc);
1265       if (rcnt)
1266       {
1267         [img release];
1268         img=newImage;    
1269       }
1270       else
1271         [newImage release];
1272     }
1273   }
1274   
1275   HGDIOBJ__ *i=GDP_OBJECT_NEW();
1276   i->type=TYPE_BITMAP;
1277   i->wid=1;
1278   i->bitmapptr = img;
1279   return i;
1282 void DrawImageInRect(HDC ctx, HICON img, const RECT *r)
1284   HGDIOBJ__ *i = (HGDIOBJ__ *)img;
1285   HDC__ *ct=(HDC__*)ctx;
1286   if (!HDC_VALID(ct) || !HGDIOBJ_VALID(i,TYPE_BITMAP) || !i->bitmapptr) return;
1287   //CGContextDrawImage(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top),(CGImage*)i->bitmapptr);
1288   // probably a better way since this ignores the ctx
1289   [NSGraphicsContext saveGraphicsState];
1290   NSGraphicsContext *gc=[NSGraphicsContext graphicsContextWithGraphicsPort:ct->ctx flipped:NO];
1291   [NSGraphicsContext setCurrentContext:gc];
1292   NSImage *nsi=i->bitmapptr;
1293   NSRect rr=NSMakeRect(r->left,r->top,r->right-r->left,r->bottom-r->top);
1294   [nsi setFlipped:YES];
1295   [nsi drawInRect:rr fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1296   [nsi setFlipped:NO]; // todo: restore old flippedness?
1297   [NSGraphicsContext restoreGraphicsState];
1298 //  [gc release];
1302 BOOL GetObject(HICON icon, int bmsz, void *_bm)
1304   memset(_bm,0,bmsz);
1305   if (bmsz < 2*(int)sizeof(LONG)) return false;
1306   BITMAP *bm=(BITMAP *)_bm;
1307   HGDIOBJ__ *i = (HGDIOBJ__ *)icon;
1308   if (!HGDIOBJ_VALID(i,TYPE_BITMAP)) return false;
1309   NSImage *img = i->bitmapptr;
1310   if (!img) return false;
1311   bm->bmWidth = (int) ([img size].width+0.5);
1312   bm->bmHeight = (int) ([img size].height+0.5);
1313   if (bmsz >= (int)sizeof(BITMAP))
1314   {
1315     bm->bmWidthBytes = bm->bmWidth * 4;
1316     bm->bmPlanes = 1;
1317     bm->bmBitsPixel = 32;
1318     bm->bmBits = NULL;
1319   }
1321   return true;
1325 void *GetNSImageFromHICON(HICON ico)
1327   HGDIOBJ__ *i = (HGDIOBJ__ *)ico;
1328   if (!HGDIOBJ_VALID(i,TYPE_BITMAP)) return 0;
1329   return i->bitmapptr;
1332 #if 0
1333 static int ColorFromNSColor(NSColor *color, int valifnul)
1335   if (!color) return valifnul;
1336   float r,g,b;
1337   NSColor *color2=[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
1338   if (!color2) 
1339   {
1340     NSLog(@"error converting colorspace from: %@\n",[color colorSpaceName]);
1341     return valifnul;
1342   }
1343   
1344   [color2 getRed:&r green:&g blue:&b alpha:NULL];
1345   return RGB((int)(r*255.0),(int)(g*255.0),(int)(b*255.0));
1347 #else
1348 #define ColorFromNSColor(a,b) (b)
1349 #endif
1350 int GetSysColor(int idx)
1352  // NSColors that seem to be valid: textBackgroundColor, selectedTextBackgroundColor, textColor, selectedTextColor
1353   
1354   switch (idx)
1355   {
1356     case COLOR_WINDOW: return ColorFromNSColor([NSColor controlColor],RGB(192,192,192));
1357     case COLOR_3DFACE: 
1358     case COLOR_BTNFACE: return ColorFromNSColor([NSColor controlColor],RGB(192,192,192));
1359     case COLOR_SCROLLBAR: return ColorFromNSColor([NSColor controlColor],RGB(32,32,32));
1360     case COLOR_3DSHADOW: return ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(96,96,96));
1361     case COLOR_3DHILIGHT: return ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(224,224,224));
1362     case COLOR_BTNTEXT: return ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(0,0,0));
1363     case COLOR_3DDKSHADOW: return (ColorFromNSColor([NSColor selectedTextBackgroundColor],RGB(96,96,96))>>1)&0x7f7f7f;
1364     case COLOR_INFOBK: return RGB(255,240,200);
1365     case COLOR_INFOTEXT: return RGB(0,0,0);
1366       
1367   }
1368   return 0;
1372 void BitBlt(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int mode)
1374   StretchBlt(hdcOut,x,y,w,h,hdcIn,xin,yin,w,h,mode);
1377 void StretchBlt(HDC hdcOut, int x, int y, int destw, int desth, HDC hdcIn, int xin, int yin, int w, int h, int mode)
1379   if (!hdcOut || !hdcIn||w<1||h<1) return;
1380   HDC__ *src=(HDC__*)hdcIn;
1381   HDC__ *dest=(HDC__*)hdcOut;
1382   if (!HDC_VALID(src) || !HDC_VALID(dest) || !src->ownedData || !src->ctx || !dest->ctx) return;
1384   if (w<1||h<1) return;
1385   
1386   const int sw = (int)CGBitmapContextGetWidth(src->ctx);
1387   const int sh = (int)CGBitmapContextGetHeight(src->ctx);
1388   
1389   const int preclip_w=w;
1390   const int preclip_h=h;
1391   
1392   if (xin<0) 
1393   { 
1394     x-=(xin*destw)/w;
1395     w+=xin; 
1396     xin=0; 
1397   }
1398   if (yin<0) 
1399   { 
1400     y-=(yin*desth)/h;
1401     h+=yin; 
1402     yin=0; 
1403   }
1404   if (xin+w > sw) w=sw-xin;
1405   if (yin+h > sh) h=sh-yin;
1406   
1407   if (w<1||h<1) return;
1409   if (destw==preclip_w) destw=w; // no scaling, keep width the same
1410   else if (w != preclip_w) destw = (w*destw)/preclip_w;
1411   
1412   if (desth == preclip_h) desth=h;
1413   else if (h != preclip_h) desth = (h*desth)/preclip_h;
1414   
1415   const bool use_alphachannel = mode == SRCCOPY_USEALPHACHAN;
1416   
1417   CGContextRef output = (CGContextRef)dest->ctx;
1418   CGRect outputr = CGRectMake(x,-desth-y,destw,desth);
1419   
1420   unsigned char *p = (unsigned char *)ALIGN_FBUF(src->ownedData);
1421   p += (xin + sw*yin)*4;
1423   
1424 #ifdef SWELL_SUPPORT_OPENGL_BLIT
1425   if (dest->GLgfxctx)
1426   {
1427     NSOpenGLContext *glCtx = (NSOpenGLContext*) dest->GLgfxctx;
1428     NSOpenGLContext *cCtx = [NSOpenGLContext currentContext];
1429     if (glCtx != cCtx)
1430     {
1431       [glCtx makeCurrentContext];
1432     }
1433     
1434     glDisable(GL_TEXTURE_2D);
1435     glEnable(GL_TEXTURE_RECTANGLE_EXT);
1436     
1437     GLuint texid=0;
1438     glGenTextures(1, &texid);
1439     glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texid);
1440     glPixelStorei(GL_UNPACK_ROW_LENGTH, sw);
1441     glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER,  GL_LINEAR);
1442     glTexImage2D(GL_TEXTURE_RECTANGLE_EXT,0,GL_RGBA8,w,h,0,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8_REV, p);
1443     
1444     glViewport(x,[[glCtx view] bounds].size.height-desth-y,destw,desth);
1445     glBegin(GL_QUADS);
1446     
1447     glTexCoord2f(0.0f, 0.0f);
1448     glVertex2f(-1,1);
1449     
1450     glTexCoord2f(0.0f, h);
1451     glVertex2f(-1,-1);
1452     
1453     glTexCoord2f(w,h);
1454     glVertex2f(1,-1);
1455     
1456     glTexCoord2f(w, 0.0f);
1457     glVertex2f(1,1);
1458     glEnd();
1459     
1460     glDeleteTextures(1,&texid);
1461     glFlush();
1463     if (glCtx != cCtx) [cCtx makeCurrentContext];
1464     return;
1465   }
1466 #endif
1467   
1468   
1469   
1470   CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,p,4*sw*h,NULL);
1471   CGImageRef img = CGImageCreate(w,h,8,32,4*sw,__GetDisplayColorSpace(),
1472       (use_alphachannel?kCGImageAlphaFirst:kCGImageAlphaNoneSkipFirst)|kCGBitmapByteOrder32Host,
1473       provider,NULL,NO,kCGRenderingIntentDefault);
1474   CGDataProviderRelease(provider);
1475   
1476   if (img)
1477   {
1478     CGContextSaveGState(output);
1479     CGContextScaleCTM(output,1.0,-1.0);
1480   
1481     CGContextSetInterpolationQuality(output,kCGInterpolationNone);
1482     CGContextDrawImage(output,outputr,img);
1483     CGContextRestoreGState(output);
1484   
1485     CGImageRelease(img);
1486   }
1489 void SWELL_PushClipRegion(HDC ctx)
1491   HDC__ *ct=(HDC__ *)ctx;
1492   if (HDC_VALID(ct) && ct->ctx) CGContextSaveGState(ct->ctx);
1495 void SWELL_SetClipRegion(HDC ctx, const RECT *r)
1497   HDC__ *ct=(HDC__ *)ctx;
1498   if (HDC_VALID(ct) && ct->ctx) CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top));
1502 void SWELL_PopClipRegion(HDC ctx)
1504   HDC__ *ct=(HDC__ *)ctx;
1505   if (HDC_VALID(ct) && ct->ctx) CGContextRestoreGState(ct->ctx);
1508 void *SWELL_GetCtxFrameBuffer(HDC ctx)
1510   HDC__ *ct=(HDC__ *)ctx;
1511   if (HDC_VALID(ct)) return ALIGN_FBUF(ct->ownedData);
1512   return 0;
1516 HDC GetDC(HWND h)
1518   if (h && [(id)h isKindOfClass:[NSWindow class]])
1519   {
1520     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1521     {
1522       PAINTSTRUCT ps={0,}; 
1523       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1524       if (ps.hdc) 
1525       {
1526         if ((ps.hdc)->ctx) CGContextSaveGState((ps.hdc)->ctx);
1527         return ps.hdc;
1528       }
1529     }
1530     h=(HWND)[(id)h contentView];
1531   }
1532   
1533   if (h && [(id)h isKindOfClass:[NSView class]])
1534   {
1535     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1536     {
1537       PAINTSTRUCT ps={0,}; 
1538       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1539       if (HDC_VALID((HDC__*)ps.hdc)) 
1540       {
1541         if (((HDC__*)ps.hdc)->ctx) CGContextSaveGState((ps.hdc)->ctx);
1542         return ps.hdc;
1543       }
1544     }
1545     
1546     if ([(NSView*)h lockFocusIfCanDraw])
1547     {
1548       HDC ret= SWELL_CreateGfxContext([NSGraphicsContext currentContext]);
1549       if (ret)
1550       {
1551         if (ret->ctx) CGContextSaveGState(ret->ctx);
1552         if (!ret->GLgfxctx && [(id)h respondsToSelector:@selector(swellGetGLContext)])
1553         {
1554           NSOpenGLContext *glctx = (NSOpenGLContext*)[(id)h swellGetGLContext];
1555           ret->GLgfxctx = glctx;
1556           if (glctx) [glctx setView:(NSView *)h];
1557         }
1558       }
1559       return ret;
1560     }
1561   }
1562   return 0;
1565 HDC GetWindowDC(HWND h)
1567   HDC ret=GetDC(h);
1568   if (ret)
1569   {
1570     NSView *v=NULL;
1571     if ([(id)h isKindOfClass:[NSWindow class]]) v=[(id)h contentView];
1572     else if ([(id)h isKindOfClass:[NSView class]]) v=(NSView *)h;
1573     
1574     if (v)
1575     {
1576       NSRect b=[v bounds];
1577       float xsc=b.origin.x;
1578       float ysc=b.origin.y;
1579       if ((xsc || ysc) && (ret)->ctx) CGContextTranslateCTM((ret)->ctx,xsc,ysc);
1580     }
1581   }
1582   return ret;
1585 void ReleaseDC(HWND h, HDC hdc)
1587   if (hdc)
1588   {
1589     if ((hdc)->ctx) CGContextRestoreGState((hdc)->ctx);
1590   }
1591   if (h && [(id)h isKindOfClass:[NSWindow class]])
1592   {
1593     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1594     {
1595       PAINTSTRUCT ps={0,}; 
1596       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1597       if (ps.hdc && ps.hdc==hdc) return;
1598     }
1599     h=(HWND)[(id)h contentView];
1600   }
1601   bool isView=h && [(id)h isKindOfClass:[NSView class]];
1602   if (isView)
1603   {
1604     if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) 
1605     {
1606       PAINTSTRUCT ps={0,}; 
1607       [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps];
1608       if (ps.hdc && ps.hdc==hdc) return;
1609     }
1610   }    
1611   if (hdc && hdc->GLgfxctx)
1612   {
1613     if ([NSOpenGLContext currentContext] == hdc->GLgfxctx) [NSOpenGLContext clearCurrentContext]; 
1614     hdc->GLgfxctx = NULL;
1615   }
1616     
1617   if (hdc) SWELL_DeleteGfxContext(hdc);
1618   if (isView && hdc)
1619   {
1620     [(NSView *)h unlockFocus];
1621 //    if ([(NSView *)h window]) [[(NSView *)h window] flushWindow];
1622   }
1625 void SWELL_FillDialogBackground(HDC hdc, const RECT *r, int level)
1627   CGContextRef ctx=(CGContextRef)SWELL_GetCtxGC(hdc);
1628   if (ctx)
1629   {
1630   // level 0 for now = this
1631     HIThemeSetFill(kThemeBrushDialogBackgroundActive,NULL,ctx,kHIThemeOrientationNormal);
1632     CGRect rect=CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top);
1633     CGContextFillRect(ctx,rect);                 
1634   }
1637 HGDIOBJ SWELL_CloneGDIObject(HGDIOBJ a)
1639   if (HGDIOBJ_VALID(a))
1640   {
1641     a->additional_refcnt++;
1642     return a;
1643   }
1644   return NULL;
1648 HBITMAP CreateBitmap(int width, int height, int numplanes, int bitsperpixel, unsigned char* bits)
1650   int spp = bitsperpixel/8;
1651   Boolean hasa = (bitsperpixel == 32);
1652   Boolean hasp = (numplanes > 1); // won't actually work yet for planar data
1653   NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:0 pixelsWide:width pixelsHigh:height 
1654                                                                bitsPerSample:8 samplesPerPixel:spp
1655                                                                hasAlpha:hasa isPlanar:hasp
1656                                                                colorSpaceName:NSDeviceRGBColorSpace
1657                                                                 bitmapFormat:NSAlphaFirstBitmapFormat 
1658                                                                  bytesPerRow:0 bitsPerPixel:0];    
1659   if (!rep) return 0;
1660   unsigned char* p = [rep bitmapData];
1661   const int pspan = (int)[rep bytesPerRow]; // might not be the same as width
1662   
1663   for (int y=0;y<height;y ++)
1664   {
1665 #ifdef __ppc__
1666     memcpy(p,bits,width*4);
1667 #else
1668     unsigned char *wr = p;
1669     const unsigned char *rd = bits;
1670     int x = width;
1671     // convert BGRA to ARGB
1672     while (x--)
1673     {
1674       wr[0] = rd[3];
1675       wr[1] = rd[2];
1676       wr[2] = rd[1];
1677       wr[3] = rd[0];
1678       wr+=4;
1679       rd+=4;
1680     }
1681 #endif
1682     p+=pspan;
1683     bits += width*4;
1684   }
1686   NSImage* img = [[NSImage alloc] init];
1687   [img addRepresentation:rep]; 
1688   [rep release];
1689   
1690   HGDIOBJ__* obj = GDP_OBJECT_NEW();
1691   obj->type = TYPE_BITMAP;
1692   obj->wid = 1; // need free
1693   obj->bitmapptr = img;
1694   return obj;
1698 HIMAGELIST ImageList_CreateEx()
1700   return (HIMAGELIST)new WDL_PtrList<HGDIOBJ__>;
1703 BOOL ImageList_Remove(HIMAGELIST list, int idx)
1705   WDL_PtrList<HGDIOBJ__>* imglist=(WDL_PtrList<HGDIOBJ__>*)list;
1706   if (imglist && idx < imglist->GetSize())
1707   {
1708     if (idx < 0) 
1709     {
1710       int x,n=imglist->GetSize();
1711       for (x=0;x<n;x++)
1712       {
1713         HGDIOBJ__ *a = imglist->Get(x);
1714         if (a) DeleteObject(a);
1715       }
1716       imglist->Empty();
1717     }
1718     else 
1719     {
1720       HGDIOBJ__ *a = imglist->Get(idx);
1721       imglist->Set(idx, NULL); 
1722       if (a) DeleteObject(a);
1723     }
1724     return TRUE;
1725   }
1726   
1727   return FALSE;
1730 void ImageList_Destroy(HIMAGELIST list)
1732   if (!list) return;
1733   ImageList_Remove(list, -1);
1734   delete (WDL_PtrList<HGDIOBJ__>*)list;
1737 int ImageList_ReplaceIcon(HIMAGELIST list, int offset, HICON image)
1739   if (!image || !list) return -1;
1740   WDL_PtrList<HGDIOBJ__> *l=(WDL_PtrList<HGDIOBJ__> *)list;
1742   HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image;
1743   if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1;
1745   HGDIOBJ__* icon=GDP_OBJECT_NEW();
1746   icon->type=TYPE_BITMAP;
1747   icon->wid=1;
1748   icon->bitmapptr = imgsrc->bitmapptr; // no need to duplicate it, can just retain a copy
1749   [icon->bitmapptr retain];
1750   image = (HICON) icon;
1752   if (offset<0||offset>=l->GetSize()) 
1753   {
1754     l->Add(image); 
1755     offset=l->GetSize()-1;
1756   }
1757   else
1758   {
1759     HICON old=l->Get(offset); 
1760     l->Set(offset,image);
1761     if (old) DeleteObject(old);
1762   }
1763   return offset;
1766 int ImageList_Add(HIMAGELIST list, HBITMAP image, HBITMAP mask)
1768   if (!image || !list) return -1;
1769   WDL_PtrList<HGDIOBJ__> *l=(WDL_PtrList<HGDIOBJ__> *)list;
1770   
1771   HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image;
1772   if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1;
1773   
1774   HGDIOBJ__* icon=GDP_OBJECT_NEW();
1775   icon->type=TYPE_BITMAP;
1776   icon->wid=1;
1777   NSImage *nsimg = [imgsrc->bitmapptr copy]; // caller still owns the image
1778   [nsimg setFlipped:YES];
1779   icon->bitmapptr = nsimg;
1780   image = (HICON) icon;
1781   
1782   l->Add(image);
1783   return l->GetSize();
1786 int AddFontResourceEx(LPCTSTR str, DWORD fl, void *pdv)
1788   if (SWELL_GDI_GetOSXVersion() < 0x1060)  return 0;
1789   static bool l;
1790   static bool (*_CTFontManagerRegisterFontsForURL)( CFURLRef fontURL, uint32_t scope, CFErrorRef *error );
1791   if (!l)
1792   {
1793     CFBundleRef b = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreText"));
1794     if (b)
1795     {
1796       *(void **)&_CTFontManagerRegisterFontsForURL = CFBundleGetFunctionPointerForName(b,CFSTR("CTFontManagerRegisterFontsForURL"));
1797     }
1799     l=true;
1800   }
1801   
1802   if (!_CTFontManagerRegisterFontsForURL) return 0;
1803   
1804   CFStringRef s=(CFStringRef)CStringToNSString(str); 
1806   CFURLRef r=CFURLCreateWithFileSystemPath(NULL,s,kCFURLPOSIXPathStyle,true);
1807   CFErrorRef err=NULL;
1808   const int v = _CTFontManagerRegisterFontsForURL(r,
1809       (fl & FR_PRIVATE) ? 1/*kCTFontManagerScopeProcess*/ : 2/*kCTFontManagerScopeUser*/,
1810       &err)?1:0;
1812   // release err? don't think so
1814   CFRelease(s);
1815   CFRelease(r);
1816   return v;
1820 #endif