OSDN Git Service

first
[psychlops/cpp.git] / psychlops / platform / osx / psychlops_g_API_Lion.mm
1 /*
2  *  psychlops_g_canvas_API_OSX.cpp
3  *  Psychlops Standard Library (MacOSX)
4  *
5  *  Last Modified 2005/12/24 by Kenchi HOSOKAWA
6  *  (C) 2005 Kenchi HOSOKAWA, Kazushi MARUYA, Takao SATO
7  */
8
9 #include <stdlib.h>
10 #include <Math.h>
11 #include <time.h>
12 #include <wchar.h>
13 #include <iostream>
14 #include <map>
15
16 #import <Cocoa/Cocoa.h>
17 #include <ApplicationServices/ApplicationServices.h>
18 #include <CoreServices/CoreServices.h>
19 #include <mach/mach.h>
20 #include <mach/mach_time.h>
21
22 #include <AGL/agl.h>
23 #include <OpenGL/OpenGL.h>
24 #include <OpenGL/gl.h>
25
26 #include "psychlops_g_API_OSX.h"
27 #include "psychlops_g_API_objc_osx.h"
28 #include "../../core/ApplicationInterfaces/psychlops_code_exception.h"
29 #include "../../core/graphic/psychlops_g_color.h"
30 #include "../../core/graphic/psychlops_g_image.h"
31 #include "../../core/graphic/psychlops_g_font.h"
32 #include "psychlops_io_display_OSX.h"
33
34
35 //#define __PSYCHLOPS__OSX_LION
36
37 namespace Psychlops {
38         
39         APICanvasProperties::APICanvasProperties() : has_instance_(false), quartz_context_(NULL) {}
40         APICanvasProperties::~APICanvasProperties() {}
41         
42         // Getting Display Settings : requires CGDirectDisplayID(the_display_), CFDictionaryRef(mode_)
43         WindowMetrix APICanvasProperties::getDisplayMetrix(CGDirectDisplayID target) {
44                 WindowMetrix tmp;
45                 tmp.width       = CGDisplayPixelsWide( target );
46                 tmp.height      = CGDisplayPixelsHigh( target );        
47 #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_6
48         CGDisplayModeRef mode = CGDisplayCopyDisplayMode(target);        
49         CFStringRef pixEnc = CGDisplayModeCopyPixelEncoding(mode);
50         if(CFStringCompare(pixEnc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
51             tmp.colordepth = 32;
52         else if(CFStringCompare(pixEnc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
53             tmp.colordepth = 16;
54         else if(CFStringCompare(pixEnc, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
55             tmp.colordepth = 8;
56 #else
57                 tmp.colordepth  = CGDisplayBitsPerPixel( target );
58 #endif
59                 tmp.refreshrate = 0.0;
60                 
61                 CFNumberRef ref_refreshrate;
62                 CFDictionaryRef mode_ = CGDisplayCurrentMode( target );
63                 ref_refreshrate = (CFNumberRef) CFDictionaryGetValue(mode_, kCGDisplayRefreshRate);
64                 if(ref_refreshrate) {
65                         CFNumberGetValue(ref_refreshrate, kCFNumberDoubleType, &(tmp.refreshrate));
66                         if(tmp.refreshrate == 0.0) tmp.refreshrate = 60.0;  // Assume LCD screen
67                 }
68                 return tmp;
69         }
70         
71         // Hardware Gamma Settings : requires CGDirectDisplayID(target_display_)
72         void APICanvasProperties::setGammaValue(const double gamma_r, const double gamma_g, const double gamma_b) {
73                 if(has_instance_) {
74                         saveGammaValue();
75                         err = CGSetDisplayTransferByFormula( target_display_,
76                                                                                                 0.0, 1.0, (1.0/gamma_r),
77                                                                                                 0.0, 1.0, (1.0/gamma_g),
78                                                                                                 0.0, 1.0, (1.0/gamma_b));
79                         //                              0.0, 1.0, (1.0/gamma_r)*savedRedGamma_,
80                         //                              0.0, 1.0, (1.0/gamma_g)*savedGreenGamma_,
81                         //                              0.0, 1.0, (1.0/gamma_b)*savedBlueGamma_);
82                         if(err!=kCGErrorSuccess) throw Exception(typeid(*this), "API ERROR", "Failed to set color calibration table for the video renderer.");
83                         gamma_mode_ = Color::GAMMA_VALUE;
84                 }
85         }
86         void APICanvasProperties::setGammaTable(const std::vector<double> &table_r, const std::vector<double> &table_g, const std::vector<double> &table_b) {
87                 if(table_r.size()!=256 || table_g.size()!=256 || table_b.size()!=256)
88                         throw Exception(typeid(*this), "Gamma Table Error", "Table size is out of order (not 256).");
89                 int num_steps = table_r.size();
90                 if(has_instance_) {
91                         saveGammaValue();
92                         CGGammaValue *(table[3]);
93                         for(int i=0; i<3; i++) table[i] = new CGGammaValue[num_steps];
94                         for(int j=0; j<num_steps; j++) {
95                                 table[0][j] = (CGGammaValue)table_r[j];
96                                 table[1][j] = (CGGammaValue)table_g[j];
97                                 table[2][j] = (CGGammaValue)table_b[j];
98                         }
99                         err = CGSetDisplayTransferByTable(target_display_, num_steps, table[0], table[1], table[2]);
100                         if(err!=kCGErrorSuccess) throw Exception(typeid(*this), "API ERROR", "Failed to set color calibration table for the video renderer.");
101                         gamma_mode_ = Color::TABLE;
102                         for(int i=0; i<3; i++) delete [] table[i];
103                 }
104         }
105         void APICanvasProperties::setGammaTable(const CGGammaValue * const table_r, const CGGammaValue * const table_g, const CGGammaValue * const table_b, const int num_steps) {
106                 if(has_instance_) {
107                         saveGammaValue();
108                         err = CGSetDisplayTransferByTable(target_display_, num_steps, table_r, table_g, table_b);
109                         gamma_mode_ = Color::TABLE;
110                 }
111         }
112         void APICanvasProperties::saveGammaValue() {
113                 if(has_instance_) {
114                         if(gamma_mode_==Color::NOTHING) {
115                                 err = CGGetDisplayTransferByFormula( target_display_,
116                                                                                                         &savedRedMin_, &savedRedMax_, &savedRedGamma_,
117                                                                                                         &savedGreenMin_, &savedGreenMax_, &savedGreenGamma_,
118                                                                                                         &savedBlueMin_, &savedBlueMax_, &savedBlueGamma_);
119                         }
120                 }
121         }
122         void APICanvasProperties::destroyGammaSettings() {
123                 if(has_instance_) {
124                         switch(gamma_mode_) {
125                                 case Color::GAMMA_VALUE:
126                                 case Color::TABLE:
127                                 default:
128                                         err = CGSetDisplayTransferByFormula( target_display_,
129                                                                                                                 savedRedMin_, savedRedMax_, savedRedGamma_,
130                                                                                                                 savedGreenMin_, savedGreenMax_, savedGreenGamma_,
131                                                                                                                 savedBlueMin_, savedBlueMax_, savedBlueGamma_);
132                                         break;
133                         }
134                         gamma_mode_ = Color::NOTHING;
135                 }
136         }
137         
138         // Letters API : requires CGContextRef(quartz_context_)
139         //void APICanvasProperties::uncacheLetters(Letters &letters) {
140         void APICanvasProperties::uncache(Letters &letters) {
141                 if(letters.caches.count(outer)!=0) {
142                         letters.caches[outer].id->uncache(quartz_context_, letters);
143                         letters.caches.erase(outer);
144                 }
145         }
146         //void APICanvasProperties::cacheLetters(Letters &letters) {
147         void APICanvasProperties::cache(Letters &letters) {
148                 if(letters.caches.count(outer)!=0) { this->uncache(letters); }
149                 letters.caches.insert(std::pair<DrawableWithCache*, Letters::Cache>(outer, Letters::Cache(new APIFontProperties, false)));
150                 letters.caches[outer].id->cache(quartz_context_, letters, *outer);
151         }
152         void APICanvasProperties::drawLetters(Letters &letters, const double x, const double y, const Color &col, const int horiz_align, const int vertical_align, const double max_width) {
153                 if(letters.caches.count(outer)==0) {
154                         outer->cacheLetters(letters);
155                 }
156 //              letters.caches[outer].id->draw(quartz_context_, letters, x,y,col,horiz_align,vertical_align,max_width, *outer);
157                 letters.caches[outer].id->draw(quartz_context_, letters, x,y,col,horiz_align,max_width, *outer);
158         }
159         
160         /*
161          void APICanvasProperties::uncacheLetters(Letters &letters) {
162          if(!letters.cache_id.empty()) {
163          delete letters.cache_id.at(0);
164          letters.cache_id.clear();
165          }
166          }
167          void APICanvasProperties::cacheLetters(Letters &letters) {
168          if(letters.cache_id.empty()) {
169          letters.cache_id.push_back(new APIFontProperties);
170          letters.cache_id.at(0)->cache(quartz_context_, letters);
171          }
172          }
173          void APICanvasProperties::drawLetters(const Letters &letters, const double x, const double y, const Color &col, const int horiz_align, const double max_width) {
174          if(!letters.cache_id.empty()) {
175          letters.cache_id.at(0)->draw(quartz_context_, letters, x, y, col, horiz_align, max_width);
176          }
177          }
178          */
179         
180         
181         ////////        APICanvasPropertiesFullscreen   ////////
182         APICanvasPropertiesFullscreen::APICanvasPropertiesFullscreen(const Display &target) {
183                 generateCanvasInstance(target);
184         }
185         APICanvasPropertiesFullscreen::APICanvasPropertiesFullscreen(int d_width, int d_height, int d_colordepth, double d_refreshrate, const Display &target) {
186                 generateCanvasInstance(d_width, d_height, d_colordepth, d_refreshrate, target);
187         }
188         APICanvasPropertiesFullscreen::~APICanvasPropertiesFullscreen() {
189                 destroyCanvasInstance();
190         }
191         
192         void APICanvasPropertiesFullscreen::generateCanvasInstance(const Display &target) {
193                 target_display_ = target.api_->did;//CGMainDisplayID();
194                 CFDictionaryRef mode_ = CGDisplayCurrentMode( target_display_ );
195                 generateCanvasInstance(mode_, target_display_);
196         }
197         void APICanvasPropertiesFullscreen::generateCanvasInstance(int d_width, int d_height, int d_colordepth, double d_refreshrate, const Display &target) {
198                 // note: true refresh rate is not integer
199                 target_display_ = target.api_->did;//CGMainDisplayID();
200                 boolean_t exactMatch;
201                 CFDictionaryRef mode_ = CGDisplayBestModeForParametersAndRefreshRate(target_display_, (size_t)d_colordepth, (size_t)d_width, (size_t)d_height, (CGRefreshRate)d_refreshrate, &exactMatch );
202                 if(!exactMatch) throw Exception(typeid(*this), "API Error", "Specified display mode was not available.");
203                 generateCanvasInstance(mode_, target_display_);
204         }
205         void APICanvasPropertiesFullscreen::generateCanvasInstance(CFDictionaryRef d_mode, CGDirectDisplayID target) {
206                 original_mode_ = CGDisplayCurrentMode( target );
207                 
208                 dispOpenGLDisplayMask = 0;
209                 CGDisplayCapture( target );
210                 CGDisplaySwitchToMode( target, d_mode );
211                 
212                 CGDisplayHideCursor( target );
213                 CGOpenGLDisplayMask displayMask = CGDisplayIDToOpenGLDisplayMask( target );
214                 WindowMetrix metrix = getDisplayMetrix(target);
215                 width_=metrix.width; height_=metrix.height; colordepth_=metrix.colordepth; refreshrate_=metrix.refreshrate;
216                 
217                 CGLPixelFormatObj dispPixFormatObj;
218                 CGLPixelFormatAttribute dispPixFormatAtt[32] = {
219                         kCGLPFADoubleBuffer,
220                         kCGLPFAFullScreen,
221                         kCGLPFANoRecovery,
222                         kCGLPFAAuxBuffers,
223                         (_CGLPixelFormatAttribute)2,
224                         kCGLPFADisplayMask,
225                         (_CGLPixelFormatAttribute)displayMask,
226                         (_CGLPixelFormatAttribute)dispOpenGLDisplayMask,
227                 };
228                 GLint syncronizeSwapIntervalWithVerticalRetraceOfTheDisplay = 1;
229                 GLint num;
230                 CGLError error;
231                 
232                 if( ! CGLChoosePixelFormat(dispPixFormatAtt, &dispPixFormatObj, &num) ) {
233                         if( ! CGLCreateContext( dispPixFormatObj, NULL, &cgl_context_) ) {
234                                 if( cgl_context_ ) {
235                                         error = CGLSetParameter( cgl_context_, kCGLCPSwapInterval, &syncronizeSwapIntervalWithVerticalRetraceOfTheDisplay );
236                                         vsync_available_ = error ? false : true;
237                                         CGLSetCurrentContext( cgl_context_ );
238                                         CGLSetFullScreen( cgl_context_ );
239                                         has_instance_ = true;
240                                 }
241                                 CGLDestroyPixelFormat( dispPixFormatObj );
242                         }
243                 }
244                 quartz_context_ = CGDisplayGetDrawingContext(target);
245                 gamma_mode_ = Color::NOTHING;
246                 CGRect rect = CGDisplayBounds(target);
247                 lefttop.set(rect.origin.x, rect.origin.y);
248         }
249         void APICanvasPropertiesFullscreen::destroyCanvasInstance() {
250                 if(has_instance_) {
251                         destroyGammaSettings();
252                         CGDisplayShowCursor( kCGDirectMainDisplay );
253                         CGLSetCurrentContext( NULL );
254                         CGLClearDrawable( cgl_context_ );
255                         CGLDestroyContext( cgl_context_ );      // error has occured
256                         
257                         CGDisplaySwitchToMode( kCGDirectMainDisplay, original_mode_ );
258                         CGDisplayRelease( kCGDirectMainDisplay );
259                         //CGDisplayErr disperr = 
260                         CGDisplayRelease( kCGDirectMainDisplay );
261                         has_instance_ = false;
262                 }
263         }
264         void APICanvasPropertiesFullscreen::flip() {
265                 CGLFlushDrawable( cgl_context_ );
266         }
267         void APICanvasPropertiesFullscreen::waitRefresh() {
268                 CGDisplayWaitForBeamPositionOutsideLines(target_display_, 1, height_-1);
269                 CGDisplayWaitForBeamPositionOutsideLines(target_display_, 0, 1);
270         }
271         Point APICanvasPropertiesFullscreen::left_top() {
272                 return lefttop;
273         }
274         
275         
276         ////////        APICanvasPropertiesWindow       ////////
277         APICanvasPropertiesWindow::APICanvasPropertiesWindow()
278         : api_objc_(0)
279         {
280         }
281         APICanvasPropertiesWindow::APICanvasPropertiesWindow(int d_width, int d_height, const Display& dd)
282         : api_objc_(0)
283         {
284                 target_display_ = dd.api_->did;
285                 generateCanvasInstance(dd.area.getLeft() + 10, dd.area.getTop() + 30, d_width, d_height);
286         }
287         APICanvasPropertiesWindow::APICanvasPropertiesWindow(int d_left, int d_top, int d_width, int d_height)
288         : api_objc_(0)
289         {
290                 target_display_ = CGMainDisplayID();
291                 generateCanvasInstance(d_left, d_top, d_width, d_height);
292         }
293         APICanvasPropertiesWindow::~APICanvasPropertiesWindow() {
294                 destroyCanvasInstance();
295                 APICanvasPropertiesObjc *api_objc = reinterpret_cast<APICanvasPropertiesObjc*>(api_objc_);
296 //              [api_objc->pool release];
297                 delete api_objc;
298                 api_objc_ = 0;
299         }
300         void APICanvasPropertiesWindow::generateCanvasInstance(int d_left, int d_top, int d_width, int d_height) {
301                 //OSStatus err = noErr;
302                 APICanvasPropertiesObjc *api_objc = new APICanvasPropertiesObjc();
303                 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
304                 //api_objc->pool = [[NSAutoreleasePool alloc] init];
305                 api_objc->app = [NSApplication sharedApplication];
306 //              [api_objc->app finishLaunching];
307 //              [api_objc->app setMainMenu: [[NSMenu alloc] initWithTitle:@"Psychlops"] ];
308                 api_objc_ = api_objc;
309
310                 //NS *window  = [[NSWindow alloc] init];
311                 
312                 const GLint swap = 1;
313                 
314                 width_ = d_width, height_ =d_height;
315                 colordepth_ = 32;
316                 refreshrate_ = 60.0;
317                 
318                 Rect rectPort;
319                 rectPort.left  = d_left;
320                 rectPort.right = rectPort.left + d_width;
321                 rectPort.top   = d_top;
322                 rectPort.bottom= rectPort.top + d_height;
323                 const Rect* rp = &rectPort;
324                 CreateNewWindow( /*kOverlayWindowClass*/kDocumentWindowClass , 0, rp, &window_ );
325                 // replace NSWindow next time
326                 
327                 GLint attributes[] =  { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 24, AGL_AUX_BUFFERS, 2, AGL_NONE };
328                 agl_context_ = NULL;
329                 
330                 AGLPixelFormat myAGLPixelFormat;
331                 myAGLPixelFormat = aglChoosePixelFormat (NULL, 0, attributes); 
332                 //err = MyAGLReportError();
333                 if (myAGLPixelFormat) { 
334                         agl_context_ = aglCreateContext (myAGLPixelFormat, NULL); 
335                         //err = MyAGLReportError (); 
336                 }
337                 if (agl_context_) {
338 #if DEBUG
339                         // if we are in debug mode
340                         aglEnable (agl_context_, AGL_FS_CAPTURE_SINGLE);
341 #else
342                         aglDisable (agl_context_, AGL_FS_CAPTURE_SINGLE);
343 #endif
344                         if(!aglSetDrawable(agl_context_, GetWindowPort(window_))) throw Exception("AGL: failed to attactch context to the window.");
345                         //                      if(!aglSetWindowRef(agl_context_, window_)) throw Exception("AGL: failed to attactch context to the window.");
346                         if(!aglSetCurrentContext(agl_context_)) throw Exception("AGL: failed to set current context.");
347                         if (!aglSetInteger (agl_context_, AGL_SWAP_INTERVAL, &swap)) throw Exception("AGL: failed to set vsync.");
348                 }
349                 // add codes to get window quartz context.
350
351                 ShowWindow(window_);
352                 CGDisplayHideCursor( target_display_ );
353         }
354         void APICanvasPropertiesWindow::destroyCanvasInstance() {
355                 if(has_instance_) {
356                         destroyGammaSettings();
357                         aglSetDrawable (agl_context_, NULL);
358                         aglSetCurrentContext (NULL);
359                         aglDestroyContext (agl_context_);               
360                         agl_context_ = NULL;
361                         DisposeWindow(window_);
362                         CGDisplayShowCursor( target_display_ );
363                         has_instance_ = false;
364                 }
365         }
366         void APICanvasPropertiesWindow::flip() {
367                 aglSwapBuffers( agl_context_ );
368         }
369         Point APICanvasPropertiesWindow::left_top() {
370                 Rect rect;
371                 GetWindowBounds(window_, kWindowContentRgn, &rect);
372                 return Point(rect.left, rect.top);
373         }
374         
375         ////////        APIFontProperties       ////////
376         
377         APIFontProperties::APIFontProperties() : cached(false), layout_defined(false), theUnicodeText(0) {
378         }
379         APIFontProperties::~APIFontProperties() {
380                 if(layout_defined) {
381                         if(theUnicodeText!=0) free(theUnicodeText);
382                         ATSUDisposeStyle(theStyle);
383                         ATSUDisposeTextLayout(theLayout);
384                         layout_defined = false;
385                 }
386                 if(cached) {
387                         cache_img.release();
388                 }
389         }
390         void APIFontProperties::cache(const CGContextRef &ctx, Letters &letters, DrawableWithCache &target) {
391                 cache_on_image(letters, target);
392         }
393         void APIFontProperties::uncache(const CGContextRef &ctx, Letters &letters) {
394                 //cache_img.uncache();
395                 cache_img.release();
396                 cached = false;
397         }
398         void APIFontProperties::draw(const CGContextRef &ctx, const Letters &letters, const double x, const double y, const Color &col, const int horiz_align, const double max_width, DrawableWithCache &target) {
399                 draw_cached_image(x, y, col, target);
400         }
401         void APIFontProperties::setLayout(Letters &letters) {
402                 if(!layout_defined) {
403 #if defined(PANTHER) && ( PANTHER ==1 )
404                         CFStringRef str = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.str.c_str()), letters.str.length()*4, kCFStringEncodingUnicode, false);
405                         CFStringRef fontfaceName = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.font.family.at(0).c_str()), letters.font.family.at(0).length()*4, kCFStringEncodingUnicode, false);
406 #else
407                         CFStringRef str = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.str.c_str()), letters.str.length()*4, kCFStringEncodingUTF32LE, false);
408                         CFStringRef fontfaceName = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.font.family.at(0).c_str()), letters.font.family.at(0).length()*4, kCFStringEncodingUTF32LE, false);
409 #endif
410                         //CFShowStr(str);
411                         const char * fontName = CFStringGetCStringPtr(fontfaceName, kCFStringEncodingMacRoman);
412                         int fontNameLength = CFStringGetLength(fontfaceName);
413                         
414                         OSStatus err;
415                         size_t textLength = CFStringGetLength(str);
416                         if(theUnicodeText!=0) free(theUnicodeText);
417                         theUnicodeText = (UniChar*)calloc(textLength, sizeof(UniChar));
418                         CFStringGetCharacters(str, CFRangeMake(0, textLength), theUnicodeText);
419                         
420                         ATSUFontID atsuFontID;
421                         err = ATSUFindFontFromName(fontName, fontNameLength, kFontPostscriptName, kFontNoPlatformCode, kFontNoScriptCode, kFontNoLanguageCode, &atsuFontID);
422                         
423                         Fixed atsuSize = FloatToFixed(letters.font.size);
424                         
425                         
426                         ATSUAttributeTag theTags[] = {
427                                 kATSUFontTag, kATSUSizeTag, kATSUVerticalCharacterTag,
428                                 kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag,
429                                 kATSUQDCondensedTag, kATSUQDExtendedTag
430                         };
431                         ByteCount theSizes[] = {
432                                 sizeof(ATSUFontID), sizeof(Fixed), sizeof(UInt16),
433                                 sizeof(Boolean), sizeof(Boolean), sizeof(Boolean),
434                                 sizeof(Boolean), sizeof(Boolean)
435                         };
436                         ATSUAttributeValuePtr theValues[] = {
437                                 NULL, NULL, NULL, NULL,
438                                 NULL, NULL, NULL, NULL
439                         };
440                         
441                         Boolean trueV=true, falseV=false;
442                         short atsuOrientation = kATSUStronglyHorizontal;
443                         theValues[0] = &atsuFontID;
444                         theValues[1] = &atsuSize;
445                         theValues[2] = &atsuOrientation;
446                         theValues[3] = (letters.font.weight==Font::normal_weight ? &falseV : &trueV);
447                         theValues[4] = (letters.font.style==Font::normal_style ? &falseV : &trueV);
448                         theValues[5] = &falseV; //underline
449                         theValues[6] = &falseV; //condense
450                         theValues[7] = &falseV; //extend
451                         
452                         ATSUCreateStyle(&theStyle);
453                         ATSUSetAttributes( theStyle, 8, theTags, theSizes, theValues );
454                         
455                         ATSUCreateTextLayoutWithTextPtr(theUnicodeText, 0, textLength, textLength, 1, &textLength, &theStyle, &theLayout);
456                         
457                         ::Rect metrix;
458                         ATSUMeasureTextImage(theLayout, 0, textLength, 0, 0, &metrix);
459                         cache_w = letters.width_ = abs(metrix.right-metrix.left);
460                         cache_h = letters.height_ = abs(metrix.bottom-metrix.top);
461                         cache_baseX = metrix.left;
462                         cache_baseY = abs(metrix.top);
463                         
464                         layout_defined = true;
465                 }
466         }
467         void APIFontProperties::cache_on_context(const CGContextRef &ctx) {
468                 if(layout_defined) {
469                         ATSUAttributeTag      theTags  [1];//2
470                         ByteCount             theSizes [1];//2
471                         ATSUAttributeValuePtr theValues[1];//2
472                         
473                         theTags  [0] = kATSUCGContextTag;
474                         theSizes [0] = sizeof(CGContextRef);
475                         theValues[0] = (void *)&ctx;
476                         ATSUSetLayoutControls(theLayout, 1, theTags, theSizes, theValues);
477                         
478                         cached = true;
479                 }
480         }
481         void APIFontProperties::draw_on_context(const CGContextRef &ctx, const float posX, const float posY, const double r,  const double g,  const double b, const double a) {
482                 if(layout_defined) {
483                         CGContextSetRGBFillColor (ctx, r, g, b, a);
484                         ATSUDrawText(theLayout, kATSUFromTextBeginning, kATSUToTextEnd, FloatToFixed(posX), FloatToFixed(posY) );
485                 }
486         }
487         void APIFontProperties::cache_on_image(Letters &letters, DrawableWithCache &target) {
488                 if(!cached) {
489                         setLayout(letters);
490                         if(cache_w<=0) cache_w=1;
491                         if(cache_h<=0) cache_h=1;
492                         cache_img.set(cache_w,cache_h,Image::RGBA);
493                         cache_img.clear(Color(0.0,0.0,0.0,0.0,false));
494                         
495                         if(cache_img.api_->pic_!=0) {
496                                 cache_on_context(cache_img.api_->pic_);
497                                 draw_on_context(cache_img.api_->pic_, -cache_baseX, cache_h-cache_baseY, 0,0,0,1);
498                         }
499
500                         // reverse xy
501                         const size_t lineb = cache_img.lineBytes_, h = cache_img.height_-1;
502                         unsigned char *tmp = new unsigned char[lineb];
503                         unsigned char *bitmapub_ = cache_img.bitmapub_;
504                         for(size_t i=0; i<(size_t)((h+1)/2); i++) {
505                                 memcpy(tmp, bitmapub_+lineb*i, lineb);
506                                 memcpy(bitmapub_+lineb*i, bitmapub_+lineb*(h-i), lineb);
507                                 memcpy(bitmapub_+lineb*(h-i), tmp, lineb);
508                         }
509                         delete [] tmp;
510                         cache_img.cache(target);
511                         cached = true;
512                 }
513         }
514         void APIFontProperties::draw_cached_image(const double posX, const double posY, const Color &col, DrawableWithCache& target) {
515                 if(cached) {
516                         const Color zero_alpha(1,1,1,0,false);
517                         glPushAttrib(GL_COLOR_BUFFER_BIT);
518                         cache_img.move_to(posX, posY-cache_baseY);
519                         
520                         // clear destination alpha
521                         glBlendFunc(GL_ZERO, GL_SRC_COLOR);
522                         cache_img.targetarea_.draw(zero_alpha, target);
523                         
524                         // draw font in alpha channel
525                         glBlendFunc(GL_ONE, GL_ONE);
526                         cache_img.draw(target);
527                         
528                         // apply alpha factor
529                         if(col.getA()!=1) {
530                                 Color alpha_mask(1,1,1,col.getA(),false);
531                                 glBlendFunc(GL_ZERO, GL_SRC_COLOR);
532                                 cache_img.targetarea_.draw(alpha_mask, target);
533                         }
534                         
535                         // drawing in specified color by destination alpha
536                         glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
537                         cache_img.targetarea_.draw(col, target);
538                         
539                         //              glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
540                         glPopAttrib();
541                 }
542         }
543         
544         
545         
546         
547         
548         ////////        APIImageProperties      ////////
549         
550         //      int APIImageProperties::max_reg = 10;
551         //      int APIImageProperties::rights_[10+1], APIImageProperties::bottoms_[10+1];
552         
553         APIImageProperties::APIImageProperties() : pic_(NULL), outer_(NULL) {
554                 //              for(int i=0; i<max_reg; i++) { rights_[i]=-1; bottoms_[i]=0; }
555                 //              rights_[0]=-1; bottoms_[0]=-1;
556         }
557         APIImageProperties::APIImageProperties(Image *outer) : pic_(NULL), outer_(outer) {
558                 attach();
559         }
560         APIImageProperties::~APIImageProperties() {
561                 detach();
562         }
563         void APIImageProperties::attach() {
564                 colorSpace = CGColorSpaceCreateDeviceRGB();
565                 if(pic_==NULL && outer_!=NULL && outer_->pixprec_==Image::BYTE && outer_->pixcomp_==Image::RGBA) {
566                         pic_ = CGBitmapContextCreate(outer_->bitmapub_, outer_->width_, outer_->height_, 8, outer_->lineBytes_, colorSpace, kCGImageAlphaPremultipliedLast);
567                         //std::cout << pic_ << std::endl;
568                 }
569         }
570         void APIImageProperties::detach() {
571                 if (pic_ != NULL) { CGContextRelease(pic_); pic_=NULL; }
572                 CGColorSpaceRelease( colorSpace );
573         }
574         /*      void APIImageProperties::drawLetters(
575          Letters &letters, const double x, const double y,
576          const double r, const double g, const double b, const double a,
577          const int horiz_align, const double max_width) {
578          if(pic_!=0) {
579          if(!letters.cache_id.empty()) {
580          letters.cache_id.push_back(new APIFontProperties);
581          CFStringRef cftext = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)(letters.str.c_str()), letters.str.length()*4, kCFStringEncodingUTF32LE, false);
582          letters.cache_id.at(0)->setLayout(cftext, letters.font);
583          letters.cache_id.at(0)->cache_on_context(pic_);
584          }
585          letters.cache_id.at(0)->draw(pic_, x, y, r, g, b, a);
586          }
587          }
588          */
589         unsigned int APIImageCache::getTexIndex() {
590                 return VRAMoffset;
591         }
592         /*
593          bool APIImageProperties::regist(int maxwidth, int maxheight, int width, int height, int &left, int &top) {
594          for(int i=1; i<max_reg; i++) {
595          if( (rights_[i]+width <= maxwidth && (bottoms_[i-1]+height<=bottoms_[i]) || (bottoms_[i-1]+height <= maxheight && bottoms_[i+1]==0) ) ) {
596          left = rights_[i]+1;
597          top = bottoms_[i-1]+1;
598          rights_[i] = left+width-1;
599          bottoms_[i] =  (bottoms_[i-1]+height<bottoms_[i]) ? bottoms_[i] : bottoms_[i-1]+height;
600          return true;
601          }
602          }
603          throw Exception(typeid(APIImageProperties), "Memory Error", "Image accelerater failed to allocate VRAM.");
604          }
605          */
606         
607         
608 }       /*      <- namespace Psychlops  */
609