PsychError SCREENTextFont(void) { boolean doSetByName, doSetByNumber, foundFont; PsychWindowRecordType *windowRecord; PsychFontStructType *fontRecord; int oldTextFontNumber, inputTextFontNumber; char *oldTextFontName, *inputTextFontName; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for valid number of arguments PsychErrorExit(PsychRequireNumInputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(2)); //Get the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); //Save the old text size value and return it. oldTextFontNumber=windowRecord->textAttributes.textFontNumber; PsychCopyOutDoubleArg(2, FALSE, (double)oldTextFontNumber); oldTextFontName=windowRecord->textAttributes.textFontName; PsychCopyOutCharArg(1, FALSE, oldTextFontName); //Fetch and set the new font if specified by name or number PsychCheckInputArgType(2, kPsychArgOptional, PsychArgType_double | PsychArgType_char); //if the argument is there check that it is the right type. doSetByNumber= PsychCopyInIntegerArg(2, kPsychArgAnything, &inputTextFontNumber); doSetByName= PsychAllocInCharArg(2, kPsychArgAnything, &inputTextFontName); foundFont=0; if(doSetByNumber) foundFont=PsychGetFontRecordFromFontNumber(inputTextFontNumber, &fontRecord); if(doSetByName) foundFont=PsychGetFontRecordFromFontFamilyNameAndFontStyle(inputTextFontName, windowRecord->textAttributes.textStyle, &fontRecord); if(foundFont){ strncpy(windowRecord->textAttributes.textFontName, fontRecord->fontFMFamilyName, 255); windowRecord->textAttributes.textFontNumber= fontRecord->fontNumber; } return(PsychError_none); }
void PsychSetATSUStyleAttributesFromPsychWindowRecord(ATSUStyle atsuStyle, PsychWindowRecordType *winRec) { PsychFontStructType *psychFontRecord; int psychColorSize; //for ATSU attributes ATSUFontID atsuFontID; Fixed atsuFontSize; ATSURGBAlphaColor atsuFontColor; ATSStyleRenderingOptions atsuRenderOptions; GLdouble colorVector[4]; OSStatus callError; ATSUAttributeTag aaTags[] = {kATSUFontTag, kATSUSizeTag, kATSURGBAlphaColorTag, kATSUStyleRenderingOptionsTag }; ByteCount aaSizes[] = {sizeof(ATSUFontID), sizeof(Fixed), sizeof(ATSURGBAlphaColor), sizeof(ATSStyleRenderingOptions) }; ATSUAttributeValuePtr aaValue[] = {&atsuFontID, &atsuFontSize, &atsuFontColor, &atsuRenderOptions}; //set the font index PsychGetFontRecordFromFontNumber(winRec->textAttributes.textFontNumber, &psychFontRecord); if(psychFontRecord==NULL) PsychErrorExitMsg(PsychError_internal, "Failed to lookup the font from the font number"); atsuFontID=psychFontRecord->fontFMRef; //set the font size atsuFontSize=Long2Fix((long)(winRec->textAttributes.textSize)); //set the color PsychCoerceColorMode(&(winRec->textAttributes.textColor)); PsychConvertColorToDoubleVector(&(winRec->textAttributes.textColor), winRec, colorVector); atsuFontColor.red=(float)colorVector[0]; atsuFontColor.green=(float)colorVector[1]; atsuFontColor.blue=(float)colorVector[2]; atsuFontColor.alpha=(float)colorVector[3]; // Set anti-aliasing mode (on/off): Default is to leave it up to the system to decide when // to anti-alias and when not. But this flag allows to always force anti-aliasin on or off: atsuRenderOptions = kATSStyleNoOptions; if(PsychPrefStateGet_TextAntiAliasing()==0) atsuRenderOptions = kATSStyleNoAntiAliasing; if(PsychPrefStateGet_TextAntiAliasing()> 0) atsuRenderOptions = kATSStyleApplyAntiAliasing; //assign attributes to the style object callError=ATSUSetAttributes(atsuStyle, 4, aaTags, aaSizes, aaValue); }
PsychError SCREENTextFont(void) { psych_bool doSetByName, doSetByNumber, foundFont; PsychWindowRecordType *windowRecord; #if PSYCH_SYSTEM == PSYCH_OSX PsychFontStructType *fontRecord; #endif int oldTextFontNumber, inputTextFontNumber; char *oldTextFontName, *inputTextFontName; int oldTextStyle, newTextStyle; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for valid number of arguments PsychErrorExit(PsychRequireNumInputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(3)); PsychErrorExit(PsychCapNumOutputArgs(3)); //Get the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); //Save the old text font value and return it. oldTextFontNumber=windowRecord->textAttributes.textFontNumber; PsychCopyOutDoubleArg(2, FALSE, (double)oldTextFontNumber); oldTextFontName=(char*) windowRecord->textAttributes.textFontName; PsychCopyOutCharArg(1, FALSE, oldTextFontName); oldTextStyle = windowRecord->textAttributes.textStyle; PsychCopyOutDoubleArg(3, FALSE, (double) oldTextStyle); //Fetch and set the new font if specified by name or number PsychCheckInputArgType(2, kPsychArgOptional, PsychArgType_double | PsychArgType_char); //if the argument is there check that it is the right type. doSetByNumber= PsychCopyInIntegerArg(2, kPsychArgAnything, &inputTextFontNumber); doSetByName= PsychAllocInCharArg(2, kPsychArgAnything, &inputTextFontName); foundFont=0; #if PSYCH_SYSTEM == PSYCH_OSX if(doSetByNumber) { foundFont=PsychGetFontRecordFromFontNumber(inputTextFontNumber, &fontRecord); } if(doSetByName) { if (PsychCopyInIntegerArg(3, FALSE, &newTextStyle)) windowRecord->textAttributes.textStyle = newTextStyle; foundFont=PsychGetFontRecordFromFontFamilyNameAndFontStyle(inputTextFontName, windowRecord->textAttributes.textStyle, &fontRecord); } if(foundFont) { strncpy((char*) windowRecord->textAttributes.textFontName, (char*) fontRecord->fontFMFamilyName, 255); windowRecord->textAttributes.textFontNumber = fontRecord->fontNumber; windowRecord->textAttributes.textStyle = fontRecord->fontFMStyle; } else { // Font not found. Is this textrenderer 2 with a font given by name? if (doSetByName && (PsychPrefStateGet_TextRenderer() > 1)) { // Yes: Must be a special font specifier string for the renderer plugin. Just assign it directly: strncpy((char*) windowRecord->textAttributes.textFontName, inputTextFontName, 255); // Don't have a valid fontNumber: Just assign a zero... windowRecord->textAttributes.textFontNumber = 0; } else { // Restore old text style setting: windowRecord->textAttributes.textStyle = oldTextStyle; } } return(PsychError_none); #else // Special case for MS-Windows and Linux: if(doSetByNumber) printf("PTB-WARNING: Sorry, selecting font by number in Screen('TextFont') is not yet supported on Windows or Linux. Command ignored.\n"); if(doSetByName && (strncmp(windowRecord->textAttributes.textFontName, inputTextFontName, 255 )!=0)) { strncpy(windowRecord->textAttributes.textFontName, inputTextFontName, 255); windowRecord->textAttributes.textFontNumber= 0; // Set the rebuild flag: windowRecord->textAttributes.needsRebuild=TRUE; } // New and different text style provided? if (PsychCopyInIntegerArg(3, FALSE, &newTextStyle) && (windowRecord->textAttributes.textStyle != newTextStyle)) { windowRecord->textAttributes.textStyle = newTextStyle; // Set the rebuild flag: windowRecord->textAttributes.needsRebuild=TRUE; } return(PsychError_none); #endif }
PsychError SCREENTextBounds(void) { //for debugging TextEncodingBase textEncodingBase; TextEncodingVariant textEncodingVariant; TextEncodingFormat textEncodingFormat; /////// PsychWindowRecordType *winRec; char *textCString; Str255 textPString; UniChar *textUniString; OSStatus callError; PsychRectType resultPsychRect, resultPsychNormRect; ATSUTextLayout textLayout; //layout is a pointer to an opaque struct. int stringLengthChars; int uniCharBufferLengthElements, uniCharBufferLengthChars, uniCharBufferLengthBytes; ByteCount uniCharStringLengthBytes; TextToUnicodeInfo textToUnicodeInfo; TextEncoding textEncoding; ATSUStyle atsuStyle; Boolean foundFont; //for ATSU style attributes PsychFontStructPtrType psychFontRecord; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for correct the number of arguments before getting involved PsychErrorExit(PsychCapNumInputArgs(2)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(2)); //get the window pointer and the text string and check that the window record has a font set PsychAllocInWindowRecordArg(1, kPsychArgRequired, &winRec); foundFont=PsychGetFontRecordFromFontNumber(winRec->textAttributes.textFontNumber, &psychFontRecord); if(!foundFont) PsychErrorExitMsg(PsychError_user, "Attempt to determine the bounds of text with no font or invalid font number"); //it would be better to both prevent the user from setting invalid font numbers and init to the OS 9 default font. //read in the string and get its length and convert it to a unicode string. PsychAllocInCharArg(2, kPsychArgRequired, &textCString); stringLengthChars=strlen(textCString); if(stringLengthChars > 255) PsychErrorExitMsg(PsychError_unimplemented, "Cut corners and TextBounds will not accept a string longer than 255 characters"); CopyCStringToPascal(textCString, textPString); uniCharBufferLengthChars= stringLengthChars * CHAR_TO_UNICODE_LENGTH_FACTOR; uniCharBufferLengthElements= uniCharBufferLengthChars + 1; uniCharBufferLengthBytes= sizeof(UniChar) * uniCharBufferLengthElements; textUniString=(UniChar*)malloc(uniCharBufferLengthBytes); //Using a TextEncoding type describe the encoding of the text to be converteed. textEncoding=CreateTextEncoding(kTextEncodingMacRoman, kMacRomanDefaultVariant, kTextEncodingDefaultFormat); //Take apart the encoding we just made to check it: textEncodingBase=GetTextEncodingBase(textEncoding); textEncodingVariant=GetTextEncodingVariant(textEncoding); textEncodingFormat=GetTextEncodingFormat(textEncoding); //Create a structure holding conversion information from the text encoding type we just created. callError=CreateTextToUnicodeInfoByEncoding(textEncoding,&textToUnicodeInfo); //Convert the text to a unicode string callError=ConvertFromPStringToUnicode(textToUnicodeInfo, textPString, (ByteCount)uniCharBufferLengthBytes, &uniCharStringLengthBytes, textUniString); //create the text layout object callError=ATSUCreateTextLayout(&textLayout); //associate our unicode text string with the text layout object callError=ATSUSetTextPointerLocation(textLayout, textUniString, kATSUFromTextBeginning, kATSUToTextEnd, (UniCharCount)stringLengthChars); //create an ATSU style object callError=ATSUCreateStyle(&atsuStyle); callError=ATSUClearStyle(atsuStyle); //Not that we have a style object we have to set style charactersitics. These include but are more general than Font Manager styles. //ATSU Style objects have three sets of characteristics: attributes, variations, and features. //attributes are things we need to set to match OS 9 behavior, such as the font ID, size, boldness, and italicization. //features are esoteric settings which we don't need for reproducing OS 9 behavior. Whatever clearstyle sets should be fine. //font variations are axes of variation through the space of font characteristics. The font definition includes available axes of variation. Something else we can ignore for now. PsychSetATSUStyleAttributesFromPsychWindowRecord(atsuStyle, winRec); //don't bother to set the variations of the style. //don't bother to set the features of the style. //associate the style with our layout object. This call assigns a style to every character of the string to be displayed. callError=ATSUSetRunStyle(textLayout, atsuStyle, (UniCharArrayOffset)0, (UniCharCount)stringLengthChars); //Get the bounds for our text so that and create a texture of sufficient size to containt it. ATSTrapezoid trapezoid; ItemCount oActualNumberOfBounds = 0; callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 0, NULL, &oActualNumberOfBounds); if (callError || oActualNumberOfBounds!=1) { PsychErrorExitMsg(PsychError_internal, "Failed to compute bounding box in call 1 to ATSUGetGlyphBounds() (nrbounds!=1)\n"); } callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &trapezoid, &oActualNumberOfBounds); if (callError || oActualNumberOfBounds!=1) { PsychErrorExitMsg(PsychError_internal, "Failed to retrieve bounding box in call 2 to ATSUGetGlyphBounds() (nrbounds!=1)\n"); } resultPsychRect[kPsychLeft]=(Fix2X(trapezoid.upperLeft.x) < Fix2X(trapezoid.lowerLeft.x)) ? Fix2X(trapezoid.upperLeft.x) : Fix2X(trapezoid.lowerLeft.x); resultPsychRect[kPsychRight]=(Fix2X(trapezoid.upperRight.x) > Fix2X(trapezoid.lowerRight.x)) ? Fix2X(trapezoid.upperRight.x) : Fix2X(trapezoid.lowerRight.x); resultPsychRect[kPsychTop]=(Fix2X(trapezoid.upperLeft.y) < Fix2X(trapezoid.upperRight.y)) ? Fix2X(trapezoid.upperLeft.y) : Fix2X(trapezoid.upperRight.y); resultPsychRect[kPsychBottom]=(Fix2X(trapezoid.lowerLeft.y) > Fix2X(trapezoid.lowerRight.y)) ? Fix2X(trapezoid.lowerLeft.y) : Fix2X(trapezoid.lowerRight.y); PsychNormalizeRect(resultPsychRect, resultPsychNormRect); PsychCopyOutRectArg(1, FALSE, resultPsychNormRect); PsychCopyOutRectArg(2, FALSE, resultPsychRect); //release resources free((void*)textUniString); callError=ATSUDisposeStyle(atsuStyle); return(PsychError_none); }
PsychError SCREENTextBounds(void) { //for debugging TextEncodingBase textEncodingBase; TextEncodingVariant textEncodingVariant; TextEncodingFormat textEncodingFormat; /////// PsychWindowRecordType *winRec; char *textCString; Str255 textPString; UniChar *textUniString; OSStatus callError; PsychRectType resultPsychRect, resultPsychNormRect; ATSUTextLayout textLayout; //layout is a pointer to an opaque struct. int stringLengthChars; int uniCharBufferLengthElements, uniCharBufferLengthChars, uniCharBufferLengthBytes, yPositionIsBaseline; double textHeightToBaseline; ByteCount uniCharStringLengthBytes; TextToUnicodeInfo textToUnicodeInfo; TextEncoding textEncoding; ATSUStyle atsuStyle; Boolean foundFont; //for ATSU style attributes PsychFontStructPtrType psychFontRecord; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check for correct the number of arguments before getting involved PsychErrorExit(PsychCapNumInputArgs(5)); PsychErrorExit(PsychRequireNumInputArgs(2)); PsychErrorExit(PsychCapNumOutputArgs(2)); //get the window pointer and the text string and check that the window record has a font set PsychAllocInWindowRecordArg(1, kPsychArgRequired, &winRec); foundFont=PsychGetFontRecordFromFontNumber(winRec->textAttributes.textFontNumber, &psychFontRecord); if(!foundFont) PsychErrorExitMsg(PsychError_user, "Attempt to determine the bounds of text with no font or invalid font number"); //it would be better to both prevent the user from setting invalid font numbers and init to the OS 9 default font. //read in the string and get its length and convert it to a unicode string. PsychAllocInCharArg(2, kPsychArgRequired, &textCString); stringLengthChars=strlen(textCString); if(stringLengthChars < 1) PsychErrorExitMsg(PsychError_user, "You asked me to compute the bounding box of an empty text string?!? Sorry, that's a no no..."); if(stringLengthChars > 255) PsychErrorExitMsg(PsychError_unimplemented, "Cut corners and TextBounds will not accept a string longer than 255 characters"); CopyCStringToPascal(textCString, textPString); uniCharBufferLengthChars= stringLengthChars * CHAR_TO_UNICODE_LENGTH_FACTOR; uniCharBufferLengthElements= uniCharBufferLengthChars + 1; uniCharBufferLengthBytes= sizeof(UniChar) * uniCharBufferLengthElements; textUniString=(UniChar*)malloc(uniCharBufferLengthBytes); PsychCopyInDoubleArg(3, kPsychArgOptional, &(winRec->textAttributes.textPositionX)); PsychCopyInDoubleArg(4, kPsychArgOptional, &(winRec->textAttributes.textPositionY)); //Using a TextEncoding type describe the encoding of the text to be converteed. textEncoding=CreateTextEncoding(kTextEncodingMacRoman, kMacRomanDefaultVariant, kTextEncodingDefaultFormat); //Take apart the encoding we just made to check it: textEncodingBase=GetTextEncodingBase(textEncoding); textEncodingVariant=GetTextEncodingVariant(textEncoding); textEncodingFormat=GetTextEncodingFormat(textEncoding); //Create a structure holding conversion information from the text encoding type we just created. callError=CreateTextToUnicodeInfoByEncoding(textEncoding,&textToUnicodeInfo); //Convert the text to a unicode string callError=ConvertFromPStringToUnicode(textToUnicodeInfo, textPString, (ByteCount)uniCharBufferLengthBytes, &uniCharStringLengthBytes, textUniString); //create the text layout object callError=ATSUCreateTextLayout(&textLayout); //associate our unicode text string with the text layout object callError=ATSUSetTextPointerLocation(textLayout, textUniString, kATSUFromTextBeginning, kATSUToTextEnd, (UniCharCount)stringLengthChars); //create an ATSU style object callError=ATSUCreateStyle(&atsuStyle); callError=ATSUClearStyle(atsuStyle); //Not that we have a style object we have to set style charactersitics. These include but are more general than Font Manager styles. //ATSU Style objects have three sets of characteristics: attributes, variations, and features. //attributes are things we need to set to match OS 9 behavior, such as the font ID, size, boldness, and italicization. //features are esoteric settings which we don't need for reproducing OS 9 behavior. Whatever clearstyle sets should be fine. //font variations are axes of variation through the space of font characteristics. The font definition includes available axes of variation. Something else we can ignore for now. PsychSetATSUStyleAttributesFromPsychWindowRecord(atsuStyle, winRec); //don't bother to set the variations of the style. //don't bother to set the features of the style. //associate the style with our layout object. This call assigns a style to every character of the string to be displayed. callError=ATSUSetRunStyle(textLayout, atsuStyle, (UniCharArrayOffset)0, (UniCharCount)stringLengthChars); // Define the meaning of the y position of the specified drawing cursor. // We get the global setting from the Screen preference, but allow to override // it on a per-invocation basis via the optional 7th argument to 'DrawText': yPositionIsBaseline = PsychPrefStateGet_TextYPositionIsBaseline(); PsychCopyInIntegerArg(5, kPsychArgOptional, &yPositionIsBaseline); if (yPositionIsBaseline) { // Y position of drawing cursor defines distance between top of text and // baseline of text, i.e. the textheight excluding descenders of letters: // Need to compute offset via ATSU: ATSUTextMeasurement mleft, mright, mtop, mbottom; callError=ATSUGetUnjustifiedBounds(textLayout, kATSUFromTextBeginning, kATSUToTextEnd, &mleft, &mright, &mbottom, &mtop); if (callError) { PsychErrorExitMsg(PsychError_internal, "Failed to compute unjustified text height to baseline in call to ATSUGetUnjustifiedBounds().\n"); } // Only take height including ascenders into account, not the descenders. // MK: Honestly, i have no clue why this is the correct calculation (or if it is // the correct calculation), but visually it seems to provide the correct results // and i'm not a typographic expert and don't intend to become one... textHeightToBaseline = fabs(Fix2X(mbottom)); } else { // Y position of drawing cursor defines top of text, therefore no offset (==0) needed: textHeightToBaseline = 0; } //Get the bounds for our text so that and create a texture of sufficient size to containt it. ATSTrapezoid trapezoid; ItemCount oActualNumberOfBounds = 0; callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 0, NULL, &oActualNumberOfBounds); if (callError || oActualNumberOfBounds!=1) { PsychErrorExitMsg(PsychError_internal, "Failed to compute bounding box in call 1 to ATSUGetGlyphBounds() (nrbounds!=1)\n"); } callError=ATSUGetGlyphBounds(textLayout, 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &trapezoid, &oActualNumberOfBounds); if (callError || oActualNumberOfBounds!=1) { PsychErrorExitMsg(PsychError_internal, "Failed to retrieve bounding box in call 2 to ATSUGetGlyphBounds() (nrbounds!=1)\n"); } resultPsychRect[kPsychLeft]=(Fix2X(trapezoid.upperLeft.x) < Fix2X(trapezoid.lowerLeft.x)) ? Fix2X(trapezoid.upperLeft.x) : Fix2X(trapezoid.lowerLeft.x); resultPsychRect[kPsychRight]=(Fix2X(trapezoid.upperRight.x) > Fix2X(trapezoid.lowerRight.x)) ? Fix2X(trapezoid.upperRight.x) : Fix2X(trapezoid.lowerRight.x); resultPsychRect[kPsychTop]=(Fix2X(trapezoid.upperLeft.y) < Fix2X(trapezoid.upperRight.y)) ? Fix2X(trapezoid.upperLeft.y) : Fix2X(trapezoid.upperRight.y); resultPsychRect[kPsychBottom]=(Fix2X(trapezoid.lowerLeft.y) > Fix2X(trapezoid.lowerRight.y)) ? Fix2X(trapezoid.lowerLeft.y) : Fix2X(trapezoid.lowerRight.y); PsychNormalizeRect(resultPsychRect, resultPsychNormRect); resultPsychRect[kPsychLeft]=resultPsychNormRect[kPsychLeft] + winRec->textAttributes.textPositionX; resultPsychRect[kPsychRight]=resultPsychNormRect[kPsychRight] + winRec->textAttributes.textPositionX; resultPsychRect[kPsychTop]=resultPsychNormRect[kPsychTop] + winRec->textAttributes.textPositionY - textHeightToBaseline; resultPsychRect[kPsychBottom]=resultPsychNormRect[kPsychBottom] + winRec->textAttributes.textPositionY - textHeightToBaseline; PsychCopyOutRectArg(1, FALSE, resultPsychNormRect); PsychCopyOutRectArg(2, FALSE, resultPsychRect); //release resources free((void*)textUniString); callError=ATSUDisposeStyle(atsuStyle); return(PsychError_none); }