diff -r 2717213c588a -r 9f66f99ee56f fbs/fontandbitmapserver/tfbs/tfbsglyphdata.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fbs/fontandbitmapserver/tfbs/tfbsglyphdata.cpp Fri Sep 24 16:14:28 2010 +0300 @@ -0,0 +1,3229 @@ +// Copyright (c) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +/** + @file + @internalComponent - Internal Symbian test code +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "FbsMessage.h" +#include "tfbsglyphdata.h" + +_LIT(KTypefaceName, "DejaVu Sans Condensed"); +//_LIT(KMonoTypefaceName, "DejaVu Sans Mono"); +const TInt KNumGlyphCodesLatin = 96; +const TUint KDejaVuInvalidGlyphCode = 0; + +// Currently only used in debug. When TestMultithreadStressAtlas() test is enabled, #ifdef to be removed. +#ifdef _DEBUG +const TInt KTestThreadMinHeapSize = 0x20000; +const TInt KTestThreadMaxHeapSize = 0x20000; +#endif + +// 'most significant bit' flag to ensure value is interpreted as a glyph code rather than an ascii code +const TUint KGlyphCodeFlag = 0x80000000; + + +// Please note the following macros which enable helper functions, and are declared in the header. +// SAVEGLYPHSTOMBMDURINGCOMPARISON and +// SAVEGLYPHSTOMBMDEBUGFUNCTION + +/* +Lookup table to convert from ascii code to +glyph code for the Deja Vu family of fonts. + */ +const TUint DejaVuASCIIToGlyphCode[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, + }; + +// Utility function declations - utilities used by the tests +static TFontSpec GenerateDejaVuFontSpec(TInt aSeed); +static void CopyCharLine(TUint32*& aBinaryDataPtr,TInt aBufferWords,const TUint8* aData,TInt aBitShift,TInt aCharWidth, TInt16 aRepeatCount); +static void DecodeBinaryData(const TSize& aDataSize, const TUint8* aData, TInt aStride, TUint32* aBinaryData); +static TInt CreateSgImageFromCharacterData(const TUint8* aData, const TSize& aSize, TGlyphBitmapType aType, RSgImage& aImage); +static TInt CreateSgImageFromCharacterData(const TUint8* aData, const TSize& aSize, TGlyphBitmapType aType, RSgImage& aImage, TUint8* aBuffer1, TUint8* aBuffer2); +// Following functions commented out because the tests which use these functions +// are currently commented out due to Broadcom defect +// ESLM-85LDV7 - TB10.1 Closing of RSgImage with duplicate handle used in same thread does not release GPU RAM +//static TInt FillGraphicsMemoryWithImages(const TSize& aSize, RArray& aImages); +//static TInt NearlyFillGraphicsMemoryWithImages(const TSize& aSize, RArray& aImages); + +#if defined (SAVEGLYPHSTOMBMDEBUGFUNCTION) || defined (SAVEGLYPHSTOMBMDURINGCOMPARISON) +/** +Static utility function. Converts an A8 RSgImage into a CFbsBitmap. +To do this, the RSgImage is converted to an EGLImage, then to a VGImage, +where the image memory is read into a CFbsBitmap. + +@param aEGL The EGL helper object that will read the SgImage into a memory buffer. +@param aSgImage The RSgImage to convert. +@param aRect A rectangular region of the RSgImage to convert. +@param aBitmap On success, holds a pointer to a CFbsBitmap which contains the image + data of the RSgImage. +@return One of the system-wide error codes. +*/ +static TInt CreateBitmapFromSgImage(CEGLHelper* aEGL, const RSgImage& aSgImage, const TRect& aRect, CFbsBitmap*& aBitmap) + { + TInt err = KErrNone; + const TSize bufferSize = aRect.Size(); + const TInt dataStride = bufferSize.iWidth; + + TUint8* imageBuffer = reinterpret_cast(User::AllocZ(bufferSize.iHeight * dataStride)); + if (!imageBuffer) + { + return KErrNoMemory; + } + err = aEGL->GetSgImageData(aSgImage, aRect, imageBuffer); + if (err != KErrNone) + { + User::Free(imageBuffer); + return err; + } + aBitmap = new CFbsBitmap(); + if (!aBitmap) + { + User::Free(imageBuffer); + return KErrNoMemory; + } + + err = aBitmap->Create(bufferSize, EGray256); + if (KErrNone == err) + { + TUint8* buf = imageBuffer; + aBitmap->BeginDataAccess(); + TUint8* dataAddress = reinterpret_cast(aBitmap->DataAddress()); + const TInt dataStride = aBitmap->DataStride(); + for (TInt scanline = 0; scanline < bufferSize.iHeight; scanline++) + { + Mem::Copy(dataAddress, buf, bufferSize.iWidth); + dataAddress += dataStride; + buf += bufferSize.iWidth; + } + aBitmap->EndDataAccess(EFalse); + } + else + { + delete aBitmap; + aBitmap = NULL; + } + + User::Free(imageBuffer); + return err; + } + +/** +Utility function to aid with debugging. +Saves a bitmap to file. + +@param aBmp Bitmap to save +@param aMeta Optional. If specified, it is added to the name of the bitmap file. +@param aRef Flag to show whether bitmap is a reference bitmap (ETrue) or test bitmap (EFalse). +*/ +static void SaveBmp(CFbsBitmap* aBmp, TPtrC* aMeta, TBool aRef) + { + if (!aBmp) + { + return; + } + + TBuf<256> testFileName; + if (aRef) + { + testFileName.Append(_L("Ref")); + } + else + { + testFileName.Append(_L("Test")); + } + if (aMeta) + { + testFileName.Append(*aMeta); + } + + TFileName mbmFile; + TBuf<20> testPathName; + #ifdef __WINS__ + testPathName.Append(_L("c:\\%S.mbm")); + #else + testPathName.Append(_L("e:\\%S.mbm")); + #endif + mbmFile.Format(testPathName, &testFileName); + + // As this is for debugging purposes only, doesn't matter reporting whether + // saving succeeded or not. + aBmp->Save(mbmFile); + } +#endif // SAVEGLYPHSTOMBMDEBUGFUNCTION OR SAVEGLYPHSTOMBMDURINGCOMPARISON + + +#ifdef SAVEGLYPHSTOMBMDEBUGFUNCTION +void CTFbsGlyphData::SaveRSgImagesAsMbms(CEGLHelper* aEGL, const RSgImage& aImageA, const TRect& aRectA, const RSgImage& aImageB, const TRect& aRectB ) + { + static TInt countToAppend = 0; + + CFbsBitmap* bitmap = NULL; + if (KErrNone == CreateBitmapFromSgImage(aEGL, aImageA, aRectA, bitmap)) + { + TBuf buf( _L("String") ); + buf.AppendNum( countToAppend ); + TPtrC nameAppend( buf ); + + SaveBmp(bitmap, &nameAppend, EFalse); + } + delete bitmap; + bitmap = NULL; + + if (KErrNone == CreateBitmapFromSgImage(aEGL, aImageB, aRectB, bitmap)) + { + TBuf buf( _L("String") ); + buf.AppendNum( countToAppend ); + TPtrC nameAppend( buf ); + + SaveBmp(bitmap, &nameAppend, ETrue); + } + delete bitmap; + bitmap = NULL; + + countToAppend++; + } + +/** +Static debug utility method that outputs the glyph images of the given glyph +codes for the given font to a file. + */ +static void DumpFontGlyphs(CEGLHelper* aEGL, CFont* aFont, TInt aCodesCount) + { + TFontSpec fontSpec = aFont->FontSpecInTwips(); + TOpenFontCharMetrics charMetrics; + TSize bitmapSize; + const TUint8* bitmapData = NULL; + + for (TInt glyphCode = 0; glyphCode < aCodesCount; glyphCode++) + { + CFont::TCharacterDataAvailability availability = aFont->GetCharacterData(glyphCode | KGlyphCodeFlag, charMetrics, bitmapData, bitmapSize); + if (availability == CFont::EAllCharacterData) + { + RSgImage characterDataImage; + TInt err = CreateSgImageFromCharacterData(bitmapData, bitmapSize, fontSpec.iFontStyle.BitmapType(), characterDataImage); + if (err == KErrNone) + { + CFbsBitmap* bitmap = NULL; + err = CreateBitmapFromSgImage(aEGL, characterDataImage, TRect(TPoint(0, 0), bitmapSize), bitmap); + if (err == KErrNone) + { + TBuf<256> bitmapName; + bitmapName.AppendFormat(_L("%S-%i"), &(fontSpec.iTypeface.Name()), glyphCode); + TPtrC bitmapNamePtr(bitmapName); + SaveBmp(bitmap, &bitmapNamePtr, EFalse); + delete bitmap; + } + } + characterDataImage.Close(); + } + } + } +#endif // SAVEGLYPHSTOMBMDEBUGFUNCTION + + +/** +Utility to return a fontspec such that the font created from it will +not match any other font generated by a different seed. The font +will be useable by RFbsGlyphDataIterator and RFbsGlyphMetricsArray. +It will always return a font based on the DejaVu fontspec, this is +so that the glyphcodes in DejaVuASCIIToGlyphCode are guaranteed to +work. + +@param aSeed Specifies a variant of the fontspec to create. Passing the + same seed will cause the same TFontSpec to be returned. +@return The generated fontspec. + */ +static TFontSpec GenerateDejaVuFontSpec(TInt aSeed) + { + const TInt KFontHeightStep = 4; + const TInt KFontInitialHeight = 8; + + const TInt KNumFontTypefaces = 3; + const TInt KNumFontBitmapTypes = 2; + const TInt KNumFontStyles = 4; + + TInt fontBitmapTypeVariant = aSeed % KNumFontBitmapTypes; + TInt fontStyleVariant = (aSeed / KNumFontBitmapTypes) % KNumFontStyles; + TInt fontTypefaceVariant = (aSeed / ( KNumFontStyles * KNumFontBitmapTypes)) % KNumFontTypefaces; + TInt fontHeightVariant = aSeed / (KNumFontStyles * KNumFontTypefaces * KNumFontBitmapTypes); + + TFontSpec fontSpec; + fontSpec.iHeight = KFontInitialHeight + (fontHeightVariant * KFontHeightStep); + // Set the typeface name + // Set the style. + switch (fontStyleVariant) + { + case 1: // italic + fontSpec.iFontStyle.SetPosture(EPostureItalic); + fontSpec.iFontStyle.SetStrokeWeight(EStrokeWeightNormal); + break; + case 2: // bold + fontSpec.iFontStyle.SetPosture(EPostureUpright); + fontSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold); + break; + case 3: // bold italic + fontSpec.iFontStyle.SetPosture(EPostureItalic); + fontSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold); + break; + default: // normal + fontSpec.iFontStyle.SetPosture(EPostureUpright); + fontSpec.iFontStyle.SetStrokeWeight(EStrokeWeightNormal); + break; + } + switch (fontTypefaceVariant) + { + case 1: + fontSpec.iTypeface.SetName(_L("DejaVu Sans Mono")); + break; + case 2: + fontSpec.iTypeface.SetName(_L("DejaVu Serif Condensed")); + break; + case 3: + fontSpec.iTypeface.SetName(_L("DejaVu Sans Condensed")); + break; + } + switch(fontBitmapTypeVariant) + { + case 1: + fontSpec.iFontStyle.SetBitmapType(EMonochromeGlyphBitmap); + break; + default: + fontSpec.iFontStyle.SetBitmapType(EAntiAliasedGlyphBitmap); + break; + } + + return fontSpec; + } + + +/** + * + EGL helper class to retrieve image data from an SgImage into a memory buffer. + */ +CEGLHelper::CEGLHelper() : + iDisplay(EGL_NO_DISPLAY), + iContext(EGL_NO_CONTEXT), + iSurface(EGL_NO_SURFACE) + { + } +CEGLHelper::~CEGLHelper() + { + iMutex.Close(); + eglMakeCurrent(iDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(iDisplay, iContext); + eglDestroySurface(iDisplay, iSurface); + eglTerminate(iDisplay); + eglReleaseThread(); + iSgDriver.Close(); + } + +/** +Factory method to create CEGLHelper. +@return A pointer to an instance of CEGLHelper. + */ +CEGLHelper* CEGLHelper::NewL() + { + CEGLHelper* self = new CEGLHelper(); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(1); // self + return self; + } + +/** +Opens handle to the process-wide synchronisation semaphore, +loads EGL and VG extension function pointers, +sets up EGL resources so that EGLImages can be constructed. + */ +void CEGLHelper::ConstructL() + { + _LIT(KEGLMutex, "TFbsGlyphDataEGLMutex"); + User::LeaveIfError(iMutex.CreateGlobal(KEGLMutex, EOwnerProcess)); + User::LeaveIfError(iSgDriver.Open()); + + iDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (iDisplay == EGL_NO_DISPLAY) + { + User::Leave(KErrNotSupported); + } + if (EGL_TRUE != eglInitialize(iDisplay, NULL, NULL)) + { + User::Leave(KErrNotSupported); + } + eglBindAPI(EGL_OPENVG_API); + + // Load the necessary EGL extensions... + eglCreateImageKHR = reinterpret_cast(eglGetProcAddress("eglCreateImageKHR")); + eglDestroyImageKHR = reinterpret_cast(eglGetProcAddress("eglDestroyImageKHR")); + vgCreateImageTargetKHR = reinterpret_cast(eglGetProcAddress("vgCreateEGLImageTargetKHR")); + if (!eglCreateImageKHR || !eglDestroyImageKHR || !vgCreateImageTargetKHR) + { + User::Leave(KErrExtensionNotSupported); + } + + // In order to create VGImages from EGLImages, a context must be current. + // Therefore create an EGLContext and EGLSurface to make current, using + // a dummy RSgImage. + + RSgImage dummySurface; + TSgImageInfo dummySurfaceInfo(TSize(1, 1), ESgPixelFormatRGB_565, ESgUsageBitOpenVgSurface); + User::LeaveIfError(dummySurface.Create(dummySurfaceInfo, NULL, 0)); + CleanupClosePushL(dummySurface); + + EGLint configAttribs[] = + { + EGL_MATCH_NATIVE_PIXMAP, (EGLint)&dummySurface, + EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, + EGL_NONE + }; + + EGLint configId = 0; + EGLint numConfigs = 0; + if (EGL_FALSE == eglChooseConfig(iDisplay, configAttribs, &configId, 1, &numConfigs) || numConfigs == 0) + { + User::Leave(KErrGeneral); + } + iContext = eglCreateContext(iDisplay, configId, EGL_NO_CONTEXT, NULL); + if (iContext == EGL_NO_CONTEXT) + { + User::Leave(KErrGeneral); + } + iSurface = eglCreatePixmapSurface(iDisplay, configId, &dummySurface, NULL); + if (iSurface == EGL_NO_SURFACE) + { + User::Leave(KErrGeneral); + } + CleanupStack::PopAndDestroy(1); // dummySurface + } + +/** +Retrieves the data from an A8 RSgImage into a buffer. +To do this, the RSgImage is converted to an EGLImage, then to a VGImage, +where the image memory is read into the given buffer. +The function can be called from multiple threads and synchronisation +with EGL is controlled via a mutex. + +@param aSgImage The RSgImage to convert. +@param aRect A rectangular region of the RSgImage to convert. +@param aBuf On success, contains the image data of the RSgImage. +@return One of the system-wide error codes. + */ +TInt CEGLHelper::GetSgImageData(const RSgImage& aSgImage, const TRect& aRect, TUint8*& aBuf) + { + const TSize bufferSize = aRect.Size(); + const TInt dataStride = bufferSize.iWidth; + + if (bufferSize == TSize(0,0)) + { + return KErrNone; + } + iMutex.Wait(); + + TInt err = KErrNone; + EGLImageKHR eglImage; + if (EGL_FALSE == eglBindAPI(EGL_OPENVG_API)) + { + err = KErrGeneral; + } + else if (EGL_FALSE == eglMakeCurrent(iDisplay, iSurface, iSurface, iContext)) + { + err = KErrGeneral; + } + else + { + // Create EGLImages from the RSgImage. + EGLint imageAttribs[] = + { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE + }; + eglImage = eglCreateImageKHR(iDisplay, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, reinterpret_cast(&aSgImage), imageAttribs); + if (eglImage == EGL_NO_IMAGE_KHR) + { + err = KErrArgument; + } + } + if (err == KErrNone) + { + // Create VGImages from the EGLImage. + VGImage vgImage = vgCreateImageTargetKHR(eglImage); + eglDestroyImageKHR(iDisplay, eglImage); + if (vgImage == VG_INVALID_HANDLE) + { + err = KErrArgument; + } + else + { + // Get the image data in 8bpp format + vgGetImageSubData(vgImage, aBuf, dataStride, VG_A_8, aRect.iTl.iX, aRect.iTl.iY, bufferSize.iWidth, bufferSize.iHeight); + vgDestroyImage(vgImage); + } + } + eglReleaseThread(); + iMutex.Signal(); + return err; + } + +CTFbsGlyphData::CTFbsGlyphData(CTestStep* aStep): + CTGraphicsBase(aStep) + { + } + +void CTFbsGlyphData::ConstructL() + { + User::LeaveIfError(Logger().ShareAuto()); + User::LeaveIfError(RFbsSession::Connect()); + iFbs = RFbsSession::GetSession(); + iTs = (CFbsTypefaceStore*)CFbsTypefaceStore::NewL(NULL); + User::LeaveIfError(iTs->GetNearestFontToDesignHeightInPixels((CFont*&)iFont, TFontSpec(KTypefaceName, 15))); + User::LeaveIfError(iTs->GetNearestFontToDesignHeightInPixels((CFont*&)iFont2, TFontSpec(KTypefaceName, 8))); + + iGlyphCodesLatin = new(ELeave) TUint[KNumGlyphCodesLatin]; + for (TInt ii = 0; ii < KNumGlyphCodesLatin; ++ii) + { + TUint asciiCode = ii+0x20; // ASCII characters from 0020 to 007F + iGlyphCodesLatin[ii] = DejaVuASCIIToGlyphCode[asciiCode]; + } + + User::LeaveIfError(iSgDriver.Open()); + iEGL = CEGLHelper::NewL(); + + // Creating a CFbsBitmap will force the RFbsSession to allocate a scanline buffer + // now rather than in the middle of a test, thus avoiding heap check failure. + CFbsBitmap* dummyBitmap = new (ELeave) CFbsBitmap; + CleanupStack::PushL(dummyBitmap); + User::LeaveIfError(dummyBitmap->Create(TSize(512, 1), EGray256)); + CleanupStack::PopAndDestroy(dummyBitmap); + + INFO_PRINTF1(_L("FBSERV Glyph Data Testing")); + } + + + + +CTFbsGlyphData::~CTFbsGlyphData() + { + delete iEGL; + iSgDriver.Close(); + if (iTs) + { + iTs->ReleaseFont(iFont); + iTs->ReleaseFont(iFont2); + } + delete iTs; + delete[] iGlyphCodesLatin; + User::Free(iTempBuf1); + User::Free(iTempBuf2); + RFbsSession::Disconnect(); + } + +void CTFbsGlyphData::RunTestCaseL(TInt aCurTestCase) + { + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(KUnknownSYMTestCaseIDName); + + TRAPD(leave, + + switch(aCurTestCase) + { + case 1: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0624")); + TestConsistencyWithGetCharacterData(); + break; + case 2: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0625")); + TestInvalidGlyphCode(); + break; + case 3: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0626")); + TestGlyphMetricsArrayParameters(); + break; + case 4: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0627")); + TestGlyphMetricsArrayReuse(); + break; + case 5: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0628")); + TestGlyphDataIteratorClose(); + break; + case 6: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0629")); + TestGlyphDataIteratorSequence(); + break; + case 7: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0632")); + TestGlyphDataIteratorMultipleUsesOnMultipleFonts(); + break; + case 8: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0633")); + TestGlyphDataIteratorImageValidity(); + break; + case 9: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0634")); + TestGlyphDataIteratorOpenInvalidCode(); + break; + case 10: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0636")); + TestGlyphDataIteratorOpenTwice(); + break; + case 11: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0637")); + TestGlyphDataIteratorOpenTwiceWithDifferentFonts(); + break; + case 12: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0638")); + TestGlyphDataIteratorOpenTooBigFont(); + break; + case 13: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0640")); + TestGlyphDataIteratorOpenWithWrongArgument(); + break; + case 14: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0641")); + TestGlyphDataIteratorImageMemoryLeak(); + break; + case 15: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0662")); + TestGlyphDataIteratorNoGraphicsMemory(); + break; + case 16: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0659")); + TestGlyphDataIteratorLargeFontStress(); + break; + case 17: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0660")); + TestGlyphDataIteratorManyFontsStressL(); + break; + case 18: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0666")); + TestGlyphDataIteratorNextIsAtomic(); + break; + case 19: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0665")); + TestGlyphDataIteratorSameGlyphCodes(); + break; + case 20: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0668")); + TestGlyphDataIteratorManyArraySizes(); + break; + case 21: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0669")); + TestBitmapFontSupport(); + break; + case 22: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0671")); + TestMultithreadShareSingleFont(); + break; + case 23: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0672")); + TestMultithreadStressAtlas(); + break; + case 24: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0673")); + TestGlyphMetricsArrayHeapOOML(); + break; + case 25: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(_L("GRAPHICS-FBSERV-0674")); + TestGlyphDataIteratorHeapOOML(); + break; + default: + ((CTFbsGlyphDataStep*)iStep)->SetTestStepID(KNotATestSYMTestCaseIDName); + ((CTFbsGlyphDataStep*)iStep)->CloseTMSGraphicsStep(); + TestComplete(); + break; + } + + ); // TRAPD + + if (leave != KErrNone) + { + ERR_PRINTF2(_L("Leave %d occurred during test"), leave); + iStep->SetTestStepResult(EFail); + } + + ((CTFbsGlyphDataStep*)iStep)->RecordTestResultL(); + } + + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0624 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Shows that RFbsGlyphMetricsArray::Get() and CFont::GetCharacterData() all + provide the same metrics for the same set of glyph codes when using a CFbsFont. + Shows that RFbsGlyphDataIterator::Metrics() and CFont::GetCharacterData() + provide the same metrics for the same set of glyph codes. + +@SYMTestActions + i. Call RFbsGlyphMetricsArray::Get() for a set of glyph codes with 1 glyph code per call. + ii. Call RFbsGlyphMetricsArray::Get() for a set of glyph codes all in 1 call. + iii. Call RFbsGlyphDataIterator::Open() for a set of glyph codes. + iv. Call CFont::GetCharacterData() for the same set of glyph codes. + v. Compare the metrics for each glyph code from all calls. + +@SYMTestExpectedResults + For each glyph code, metrics received from RFbsGlyphMetricsArray::Get() and + CFont::GetCharacterData() and RFbsGlyphDataIterator are all the same. +*/ +void CTFbsGlyphData::TestConsistencyWithGetCharacterData() + { + INFO_PRINTF1(_L("Test RFbsGlyphMetricsArray::Get() with GetCharacterData()")); + + __UHEAP_MARK; + + RFbsGlyphDataIterator iter; + RFbsGlyphMetricsArray glyphMetricsArray; + RFbsGlyphMetricsArray glyphMetricsArraySingle; + + TInt numMismatches = 0; + TOpenFontCharMetrics charMetrics; + TSize bitmapSize; + const TUint8* bitmapData = NULL; + + // Retrieve list of metrics for all glyph codes in one call + TInt err = glyphMetricsArray.Get(*iFont, iGlyphCodesLatin, KNumGlyphCodesLatin); + TESTNOERROR(err); + if (err == KErrNone) + { + TEST(KNumGlyphCodesLatin == glyphMetricsArray.Count()); + + TInt index = 0; + TInt iterErr = iter.Open(*iFont, iGlyphCodesLatin, KNumGlyphCodesLatin); + TESTNOERROR(iterErr); + for (; iterErr == KErrNone; iterErr = iter.Next(), index++) + { + iFont->GetCharacterData(iGlyphCodesLatin[index] | KGlyphCodeFlag, charMetrics, bitmapData, bitmapSize); + + // Retrieve the metrics for each glyph code, one at a time + TESTNOERROR(err = glyphMetricsArraySingle.Get(*iFont, &iGlyphCodesLatin[index], 1)); + if (KErrNone == err) + { + // Compare GetCharacterData() metrics with single RFbsGlyphMetricsArray. + TUint32 comparison1 = CompareMetrics(charMetrics, glyphMetricsArraySingle[0]); + // Compare GetCharacterData() metrics with large RFbsGlyphMetricsArray. + TUint32 comparison2 = CompareMetrics(charMetrics, glyphMetricsArray[index]); + // Compare GetCharacterData() metrics with RFbsGlyphDataIterator. + TUint32 comparison3 = CompareMetrics(charMetrics, iter.Metrics()); + if (comparison1 != 0 || comparison2 != 0 || comparison3 != 0) + { + ERR_PRINTF5(_L("Glyphcode %i : Metrics mismatch: %d/%d/%d"), iGlyphCodesLatin[index], comparison1, comparison2, comparison3); + ++numMismatches; + } + } + } + iter.Close(); + glyphMetricsArray.Close(); + glyphMetricsArraySingle.Close(); + TESTE(iterErr == KErrNotFound, iterErr); + TEST(numMismatches == 0); + TEST(index == KNumGlyphCodesLatin); + } + + __UHEAP_MARKEND; + } + +/** +@return A series of success/fail booleans as a bitmask. A return value of zero + indicates all tests passed, a result of 1 indicates the first test case failed, + a return of 3 indicates the first and second test failed, and so on. +*/ +TUint32 CTFbsGlyphData::CompareMetrics(const TOpenFontCharMetrics& aMetrics1, const TOpenFontCharMetrics& aMetrics2) + { + TUint32 result = 0; + result |= (aMetrics1.Width() == aMetrics2.Width()) ? 0 : (1 << 0); + result |= (aMetrics1.Height() == aMetrics2.Height()) ? 0 : (1 << 1); + result |= (aMetrics1.HorizBearingX() == aMetrics2.HorizBearingX()) ? 0 : (1 << 2); + result |= (aMetrics1.HorizBearingY() == aMetrics2.HorizBearingY()) ? 0 : (1 << 3); + result |= (aMetrics1.HorizAdvance() == aMetrics2.HorizAdvance()) ? 0 : (1 << 4); + result |= (aMetrics1.VertBearingX() == aMetrics2.VertBearingX()) ? 0 : (1 << 5); + result |= (aMetrics1.VertBearingY() == aMetrics2.VertBearingY()) ? 0 : (1 << 6); + result |= (aMetrics1.VertAdvance() == aMetrics2.VertAdvance()) ? 0 : (1 << 7); + TRect rect1; + aMetrics1.GetHorizBounds(rect1); + TRect rect2; + aMetrics2.GetHorizBounds(rect2); + result |= (rect1 == rect2) ? 0 : (1 << 8); + aMetrics1.GetVertBounds(rect1); + aMetrics2.GetVertBounds(rect2); + result |= (rect1 == rect2) ? 0 : (1 << 9); + return result; + } + + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0625 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Shows that RFbsGlyphMetricsArray::Get(), and CFont::GetCharacterData() show the same + behaviour when asked for metrics for an invalid glyph code when using a CFbsFont. + An invalid glyph code is one for which there is no character equivalent, such as + 0. + +@SYMTestActions + i. Call CFont::GetCharacterData() for an invalid glyph code. + ii. Call RFbsGlyphMetricsArray::Get() for the same invalid glyph code, and either + compare the metrics if i. was successful, or check an error code was returned + +@SYMTestExpectedResults + If GetCharacterData() is successful, the metrics received from + RFbsGlyphMetricsArray::Get() and CFont::GetCharacterData() are the same, otherwise + RFbsGlyphMetricsArray::Get() should return an error code. +*/ + +void CTFbsGlyphData::TestInvalidGlyphCode() + { + INFO_PRINTF1(_L("Test behaviour of RFbsGlyphMetricsArray::Get() with invalid glyph code is consistent with GetCharacterData")); + + __UHEAP_MARK; + TInt arrayErr = KErrNone; + RFbsGlyphMetricsArray glyphMetricsArray; + TOpenFontCharMetrics charMetrics; + TSize bitmapSize; + const TUint8* bitmapData = NULL; + + CFont::TCharacterDataAvailability availability = iFont->GetCharacterData(KDejaVuInvalidGlyphCode | KGlyphCodeFlag, charMetrics, bitmapData, bitmapSize); + if (availability == CFont::ENoCharacterData) + { + // Some rasterizers fail to return any data for KDejaVuInvalidGlyphCode, therefore + // rather than compare metrics, make sure RFbsGlyphDataIterator returns an error code. + WARN_PRINTF1(_L("Rasterizer failed to return data for invalid glyph code; not comparing glyph metrics")); + arrayErr = glyphMetricsArray.Get(*iFont, &KDejaVuInvalidGlyphCode, 1); + TESTE(arrayErr != KErrNone, arrayErr); + } + else + { + TESTNOERROR(arrayErr = glyphMetricsArray.Get(*iFont, &KDejaVuInvalidGlyphCode, 1)); + if (KErrNone == arrayErr) + { + iFont->GetCharacterData(KDejaVuInvalidGlyphCode | KGlyphCodeFlag, charMetrics, bitmapData, bitmapSize); + TUint comparisonResult = CompareMetrics(charMetrics, glyphMetricsArray[0]); + TESTNOERROR( comparisonResult ); + } + } + glyphMetricsArray.Close(); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0626 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Shows that RFbsGlyphMetricsArray::Get() returns with the correct error code when passed + various combinations of parameters, and preserves the state of the array. + +@SYMTestActions + Populate the array with a single metrics entry. + Call RFbsGlyphMetricsArray::Get with the following parameter combinations: + 1. A negative count + 2. A positive count and null glyph code array pointer + 3. A zero count and non-null glyph code array pointer + 4. A zero count and null glyph code array pointer + +@SYMTestExpectedResults + The following return codes are expected for each call: + 1. KErrArgument + 2. KErrArgument + 3. KErrArgument + 4. KErrArgument + For each case the glyph metrics array remains unchanged. +*/ +void CTFbsGlyphData::TestGlyphMetricsArrayParameters() + { + INFO_PRINTF1(_L("Test the return values of GetGlyphMetrics with different parameters")); + __UHEAP_MARK; + TInt arrayErr = KErrNone; + TOpenFontCharMetrics dummyMetrics; + + RFbsGlyphMetricsArray glyphMetricsArray; + arrayErr = glyphMetricsArray.Get(*iFont, iGlyphCodesLatin, 1); + TESTNOERROR(arrayErr); + TEST(1 == glyphMetricsArray.Count()); + + // 1. Negative Count + arrayErr = glyphMetricsArray.Get(*iFont, iGlyphCodesLatin, -1); + TESTE(KErrArgument == arrayErr, arrayErr); + TEST(1 == glyphMetricsArray.Count()); + + // 2. Positive Count and NULL Array Pointer + arrayErr = glyphMetricsArray.Get(*iFont, NULL, 1); + TESTE(KErrArgument == arrayErr, arrayErr); + TEST(1 == glyphMetricsArray.Count()); + + // 3. Zero Count & Valid Array Pointer + arrayErr = glyphMetricsArray.Get(*iFont, iGlyphCodesLatin, 0); + TESTE(KErrArgument == arrayErr, arrayErr); + + // 4. Zero Count & NULL Array Pointer + arrayErr = glyphMetricsArray.Get(*iFont, NULL, 0); + TESTE(KErrArgument == arrayErr, arrayErr); + + glyphMetricsArray.Close(); + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0627 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Shows that reusing an RFbsGlyphMetricsArray works correctly. + In particular when the array is reused and filled with fewer entries + and when the array is reused and filled with more entries than previously. + It also shows that when re-using an array that has been populated, memory + is not de-allocated if the new array of glyphs is smaller. + +@SYMTestActions + i. Call RFbsGlyphMetricsArray::Get() for a set of 10 glyph codes. + ii. Check that the RFbsGlyphMetricsArray has 10 entries. + iii. Find the size of the heap-cell allocated to the array. + iii. Call RFbsGlyphMetricsArray::Get() for a set of 5 glyph codes. + iv. Check that the RFbsGlyphMetricsArray has 5 entries. + v. Call RFbsGlyphMetricsArray::Get() for a set of 20 glyph codes. + vi. Check that the RFbsGlyphMetricsArray has 20 entries. + vii. Call RFbsGlyphMetricsArray::Get() for a set of 0 glyph codes. + viii. Check that the RFbsGlyphMetricsArray has 0 entries. + ix. Call RFbsGlyphMetricsArray::Get() for 1 glyph code. + x. Check that the RFbsGlyphMetricsArray has 1 entries. + xi. Close the RFbsGlyphMetricsArray. + xii. Check that the RFbsGlyphMetricsArray has 0 entries. + During the test check that the size of the heap cell allocated to the array + does not shrink. + +@SYMTestExpectedResults + After each call to RFbsGlyphMetricsArray::Get(), the array has the expected number of entries. +*/ +void CTFbsGlyphData::TestGlyphMetricsArrayReuse() + { + INFO_PRINTF1(_L("Test reuse of array with RFbsGlyphMetricsArray")); + __UHEAP_MARK; + + RFbsGlyphMetricsArray glyphMetricsArray; + + // Retrieve list of metrics for 10 glyph codes + TESTNOERROR(glyphMetricsArray.Get(*iFont, iGlyphCodesLatin, 10)); + TEST(10 == glyphMetricsArray.Count()); + + // Find the size of the heap cell allocated for the array. + TInt arrayHeapCellSize = User::Heap().AllocLen(&glyphMetricsArray[0]); + + // Retrieve list of metrics for 5 glyph codes. + // To ensure that different metrics are returned, use different glyph codes + TESTNOERROR(glyphMetricsArray.Get(*iFont, &iGlyphCodesLatin[10], 5)); + TEST(5 == glyphMetricsArray.Count()); + // Check that memory has not been de-allocated for a smaller array. + TEST(User::Heap().AllocLen(&glyphMetricsArray[0]) == arrayHeapCellSize); + + // Retrieve list of metrics for 20 glyph codes. + // To ensure that different metrics are returned, use different glyph codes + TESTNOERROR(glyphMetricsArray.Get(*iFont, &iGlyphCodesLatin[15], 20)); + TEST(20 == glyphMetricsArray.Count()); + arrayHeapCellSize = User::Heap().AllocLen(&glyphMetricsArray[0]); + + // Retrieve list of metrics for 0 glyph codes. + TEST(KErrArgument == glyphMetricsArray.Get(*iFont, &iGlyphCodesLatin[35], 0)); + // We can't check whether memory has been de-allocated as glyphMetricsArray[0] + // is null, therefore dereferencing it causes a panic. + + // Retrieve list of metrics for 1 glyph code. + // To ensure that different metrics are returned, use different glyph code + TESTNOERROR(glyphMetricsArray.Get(*iFont, &iGlyphCodesLatin[35], 1)); + TEST(1 == glyphMetricsArray.Count()); + TEST(User::Heap().AllocLen(&glyphMetricsArray[0]) == arrayHeapCellSize); + + // Test that after closing a non-empty array, the array has 0 size. + glyphMetricsArray.Close(); + TEST(0 == glyphMetricsArray.Count()); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0628 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Validates the behaviour of RFbsGlyphDataIterator::Close() in the following use cases: + 1. When called on an iterator instance which has not been opened, has no effect. + 2. When called on an open iterator closes the iterator + +@SYMTestActions + Use case 1: + i. Create an RFbsGlyphDataIterator instance but do not open. + ii. Call RFbsGlyphDataIterator::Close(). + + Use case 2: + i. Create an RFbsGlyphDataIterator instance and call RFbsGlyphDataIterator::Open(). + ii. Call RFbsGlyphDataIterator::Next() to prove the iterator is open. + iii. Call RFbsGlyphDataIterator::Close(). + iv. Check that RFbsGlyphDataIterator::IsOpen() returns false. + +@SYMTestExpectedResults + Each call to RFbsGlyphDataIterator::IsOpen() returns the expected value. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorClose() + { + INFO_PRINTF1(_L("Test closing an RFbsGlyphDataIterator")); + __UHEAP_MARK; + + // Use case 1 + RFbsGlyphDataIterator iter1; + iter1.Close(); + + // Use case 2 + RFbsGlyphDataIterator iter2; + TESTNOERROR(iter2.Open(*iFont, iGlyphCodesLatin, 1)); + TInt iterErr = iter2.Next(); + TESTE(KErrNotFound == iterErr, iterErr); + iter2.Close(); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0629 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Show that the sequence of iterations when calling RFbsGlyphDataIterator::Next() + matches the order of the array of glyph codes. + +@SYMTestActions + i. Create an RFbsGlyphDataIterator instance and call RFbsGlyphDataIterator::Open() + with an array of different glyph codes. + ii. Iterate through all the glyph data. + For each iteration check that the glyph code returned from + RFbsGlyphDataIterator::GlyphCode() matches the corresponding glyph code + passed into Open(). + +@SYMTestExpectedResults + Each comparison of glyph code should match. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorSequence() + { + INFO_PRINTF1(_L("Test the iterator sequence of RFbsGlyphDataIterator")); + __UHEAP_MARK; + + TBool matches = ETrue; + TInt index = 0; + + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*iFont, iGlyphCodesLatin, KNumGlyphCodesLatin); + TESTNOERROR(iterErr); + for (; index < KNumGlyphCodesLatin && matches && (iterErr == KErrNone); iterErr = iter.Next(), index++) + { + if (iter.GlyphCode() != iGlyphCodesLatin[index]) + { + ERR_PRINTF4(_L("Failed at iteration %d: wanted %d, got %d"), index, iGlyphCodesLatin[index], iter.GlyphCode()); + matches = EFalse; + } + } + iter.Close(); + TESTE(iterErr == KErrNotFound, iterErr); + TEST(matches); + TEST(index == KNumGlyphCodesLatin); + iter.Close(); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0632 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Ensure it is possible to reuse a closed iterator on another CFbsFont. + +@SYMTestActions + i. Open an RFbsGlyphDataIterator with sample data. + ii. Iterate through until the end of the iterator has been reached by calling + Next() on the final element. + iii. Re-open the same RFbsGlyphDataIterator with sample data on a different CFbsFont. + iv. Iterate through a second time until the end has been reached by calling Next() + on the final element. + v. Close the iterator. + vi. During both iterations the bitmap data returned and metrics are compared with + the equivalent from GetCharacterData(). + +@SYMTestExpectedResults + The iterator should be opened successfully for both fonts and the data returned + should match the data from GetCharacterData(). +*/ +void CTFbsGlyphData::TestGlyphDataIteratorMultipleUsesOnMultipleFonts() + { + INFO_PRINTF1(_L("Reuse a closed iterator on a second CFbsFont")); + __UHEAP_MARK; + + const TUint8* bitmapData; + TSize bitmapSize; + TOpenFontCharMetrics charMetrics; + RFbsGlyphDataIterator iter; + + // Array of fonts to iterate through. + CFbsFont* font[2] = {iFont, iFont2}; + + for (TInt fontId = 0; fontId < 2; fontId++) + { + // On the first iteration, open and use a font until all glyphs have been iterated through. + // On second iteration, use the same iterator on a different font and repeat. + CFbsFont* currentFont = font[fontId]; + + //Open the iterator on the first font and compare the returned bitmaps against GetCharacterData + TInt iterErr = iter.Open(*currentFont, iGlyphCodesLatin, KNumGlyphCodesLatin); + TESTNOERROR(iterErr); + TFontSpec fontSpec = currentFont->FontSpecInTwips(); + + TInt index = 0; + for (; (iterErr == KErrNone) && (index < KNumGlyphCodesLatin); iterErr = iter.Next(), ++index) + { + currentFont->GetCharacterData(iGlyphCodesLatin[index] | KGlyphCodeFlag, charMetrics, bitmapData, bitmapSize); + + TESTNOERROR(CompareMetrics(charMetrics, iter.Metrics())); + if (bitmapSize == TSize(0, 0)) + { + TEST(bitmapSize == iter.Rect().Size()); + } + else + { + // Compare images. + TBool match = EFalse; + RSgImage characterDataImage; + TInt err = CreateSgImageFromCharacterData(bitmapData, bitmapSize, fontSpec.iFontStyle.BitmapType(), characterDataImage); + if (err == KErrNone) + { + err = CompareSgImages(iEGL, iter.Image(), iter.Rect(), characterDataImage, TRect(bitmapSize), match); + } + characterDataImage.Close(); + if (err != KErrNone) + { + TESTNOERROR(err); + break; + } + TEST(match); + } + } + iter.Close(); + TESTE(iterErr == KErrNotFound, iterErr); + TEST(index == KNumGlyphCodesLatin); + } + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0633 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Check that for various Latin fonts, the images of the glyphs stored on the + RSgImage matches those provided by GetCharacterData(). + +@SYMTestActions + Create a selection of fonts, using various typefaces, sizes and bitmap types. + For each font: + i. Open the RFbsGlyphDataIterator and iterate each glyph. + ii. For each glyph, call GetCharacterData() with the expected glyph code. + iii. Convert the character data to an RSgImage. + iv. Perform a comparison between the character RSgImage and the iterator + image. + v. After all iterations, close the iterator and check all expected glyphs + were iterated through. + +@SYMTestExpectedResults + All glyph images should match. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorImageValidity() + { + INFO_PRINTF1(_L("Test the glyph images of the iterator match GetCharacterData()")); + __UHEAP_MARK; + + const TInt KNumFonts = 20; + + // Create a new typeface store for this test so that heap checking will not + // be affected by cached CFbsFonts. + CFbsTypefaceStore* typefaceStore = NULL; + TRAPD(err, typefaceStore = CFbsTypefaceStore::NewL(NULL)); + if (err != KErrNone) + { + ERR_PRINTF1(_L("Failed to construct typeface store. Test aborted.")); + __UHEAP_RESET; + iStep->SetTestStepResult(EFail); + return; + } + + for (TInt font = 0; font < KNumFonts; ++font) + { + // Use either a pre-created bitmap-font TFontSpec, or generate a Deja-vu one. + TFontSpec fontSpec = GenerateDejaVuFontSpec(font); + CFbsFont* latinFont = NULL; + TESTNOERROR(typefaceStore->GetNearestFontToDesignHeightInPixels((CFont*&)latinFont, fontSpec)); + + fontSpec = latinFont->FontSpecInTwips(); + InfoPrintFontSpec(*latinFont); + + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*latinFont, iGlyphCodesLatin, KNumGlyphCodesLatin); + TESTNOERROR(iterErr); + + err = KErrNone; + TInt index = 0; + TInt numMismatches = 0; + // For each iteration, get the character data of the expected glyph. + // Create RSgImage from character data, and compare iter image with constructed image. + for (; (iterErr == KErrNone) && (err == KErrNone) && (index < KNumGlyphCodesLatin); (iterErr = iter.Next()), ++index) + { + TBool glyphMatches = ETrue; + const RSgImage& iteratorImage = iter.Image(); + + const TUint8* bitmapData = NULL; + TSize bitmapSize; + TOpenFontCharMetrics metrics; + TInt characterDataAvailability = latinFont->GetCharacterData(iGlyphCodesLatin[index] | KGlyphCodeFlag, metrics, bitmapData, bitmapSize); + if (bitmapSize == TSize(0, 0)) + { + glyphMatches = (bitmapSize == iter.Rect().Size()); + } + else + { + RSgImage characterDataImage; + TESTNOERROR(CreateSgImageFromCharacterData(bitmapData, bitmapSize, fontSpec.iFontStyle.BitmapType(), characterDataImage)); + err = CompareSgImages(iEGL, iteratorImage, iter.Rect(), characterDataImage, TRect(bitmapSize), glyphMatches); + characterDataImage.Close(); + } + if (err == KErrNone && !glyphMatches) + { + ERR_PRINTF2(_L("Glyphcode %i : Image mismatch"), iGlyphCodesLatin[index]); + ++numMismatches; + } + } + iter.Close(); + TESTNOERROR(err); + TESTE(iterErr == KErrNotFound, iterErr); + TEST(index == KNumGlyphCodesLatin); + TEST(numMismatches == 0); + typefaceStore->ReleaseFont(latinFont); + } + delete typefaceStore; + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0634 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + To ensure that if the glyph image iterator has a current invalid + character code, the SgImage returned by the iterator will match + to the image obtained from the GetCharacterData() function +@SYMTestActions + i. Retrieve bitmap data and metrics by using GetCharacterData(). + ii. Open a glyph data iterator passing an invalid character code. + iii. If i. was unsuccessful, check that opening the iterator returned + an error code and skip to ix. + iv. Create SgImage from bitmap data. + v. Get SgImage from the glyph data iterator. + vi. Compare SgImages obtained on iv and v steps. + vii. Get font metrics from the glyph data iterator. + viii. Compare metrics obtained on i and vii steps. + vii. Close the iterator. + +@SYMTestExpectedResults + If the request to get the character data failed, the return value of + RFbsGlyphDataIterator::Open() must not be KErrNone. + Otherwise, images obtained from the iterator and GetCharacterData() should + match. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorOpenInvalidCode() + { + INFO_PRINTF1(_L("Ensure that the image returned by the iterator will \ +match to the image obtained from GetCharacterData() if character code is invalid")); + __UHEAP_MARK; + + const TUint8* bitmapData = NULL; + TSize bitmapSize; + TOpenFontCharMetrics metrics; + const TFontSpec fontSpec = iFont->FontSpecInTwips(); + CFont::TCharacterDataAvailability availability = iFont->GetCharacterData(KDejaVuInvalidGlyphCode | KGlyphCodeFlag, metrics, bitmapData, bitmapSize); + + RFbsGlyphDataIterator iter; + TInt err = iter.Open(*iFont, &KDejaVuInvalidGlyphCode, 1); + if (availability == CFont::ENoCharacterData) + { + // Some rasterizers fail to return any data for KDejaVuInvalidGlyphCode, therefore + // rather than compare image contents, make sure RFbsGlyphDataIterator returns an error code. + WARN_PRINTF1(_L("Rasterizer failed to return data for invalid glyph code; not comparing image contents")); + TESTE(err != KErrNone, err); + } + else + { + TESTNOERROR(err); + if (err == KErrNone) + { + TBool glyphMatches = EFalse; + if (bitmapSize == TSize(0, 0)) + { + glyphMatches = (bitmapSize == iter.Rect().Size()); + } + else + { + RSgImage characterDataImage; + TESTNOERROR(CreateSgImageFromCharacterData(bitmapData, bitmapSize, fontSpec.iFontStyle.BitmapType(), characterDataImage)); + TESTNOERROR(CompareSgImages(iEGL, iter.Image(), iter.Rect(), characterDataImage, TRect(bitmapSize), glyphMatches)); + characterDataImage.Close(); + } + TESTNOERROR(CompareMetrics(metrics, iter.Metrics())); + TEST(glyphMatches); + } + } + iter.Close(); + + __UHEAP_MARKEND; + } + + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0636 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + To ensure that opening the glyph data iterator which has already been opened with the same font + has no effect on the state of the iterator. +@SYMTestActions + i. Open glyph data iterator on 2 glyph codes. + ii. Try to open the glyph data iterator again on the same font. + iii. Call RFbsGlyphDataIterator::Next() on the iterator and check error code, making the last + glyph code the current iteration. + iv. Call RFbsGlyphDataIterator::Next() again. +@SYMTestExpectedResults + The second attempt to open the glyph data iterator will result an error with code KErrInUse. + The last two calls to RFbsGlyphDataIterator::Next() should return KErrNone and KErrNotFound + respectively, showing the iterator was not modified when the call to Open() failed. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorOpenTwice() + { + INFO_PRINTF1(_L("Ensure that opening the glyph data iterator which has already been opened with the same font has no effect")); + __UHEAP_MARK; + + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*iFont, iGlyphCodesLatin, 2); + TESTNOERROR(iterErr); + + iterErr = iter.Open(*iFont, iGlyphCodesLatin, KNumGlyphCodesLatin); + TESTE(iterErr == KErrInUse, iterErr); + iterErr = iter.Next(); + TESTNOERROR(iterErr); + iterErr = iter.Next(); + TESTE(iterErr == KErrNotFound, iterErr); + iter.Close(); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0637 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + To ensure that opening the glyph data iterator which has already been opened with different font + has no effect on the state of the iterator. +@SYMTestActions + i. Open glyph data iterator on an 2 glyph codes + ii. Try to open the glyph data iterator again with a different font. + iii. Call RFbsGlyphDataIterator::Next() on the iterator and check error code, making the last + glyph code the current iteration. + iv. Call RFbsGlyphDataIterator::Next() again. +@SYMTestExpectedResults + The second attempt to open the glyph data iterator will result an error with code KErrInUse. + The Next() call after this should return KErrNone, signifying the iterator is still open. + The last Next() call should return KErrNotFound, signifying the iterator has iterated + through the two original glyph codes. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorOpenTwiceWithDifferentFonts() + { + INFO_PRINTF1(_L("Ensure that opening the glyph data iterator which has already been opened with different font has no effect")); + __UHEAP_MARK; + + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*iFont, iGlyphCodesLatin, 2); + TESTNOERROR(iterErr); + + iterErr = iter.Open(*iFont2, iGlyphCodesLatin, 2); + TESTE(iterErr == KErrInUse, iterErr); + iterErr = iter.Next(); + TESTNOERROR(iterErr); + iterErr = iter.Next(); + TESTE(iterErr == KErrNotFound, iterErr); + iter.Close(); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0638 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + To ensure that opening of glyph data iterator with the font greater than + 2048 by 2048 will not be supported +@SYMTestActions + i. Create font with the height greater than 2048 + ii. Try to open the glyph data iterator with the font created on previous step + iii Release the font +@SYMTestExpectedResults + Must fail with error code KErrTooBig +*/ +void CTFbsGlyphData::TestGlyphDataIteratorOpenTooBigFont() + { + INFO_PRINTF1(_L("To ensure that opening of glyph data iterator with the font greater than 2048X2048 will not be supported")); + __UHEAP_MARK; + + CFbsFont* bigFont; + const TInt maxHeight = 2048; + const TInt maxHeightLimit = maxHeight + 20; //max size after we stop trying to create the font + // the loop below will guarantee that if the font with the size greater than 2048 is available it will be created + for(TInt height = maxHeight + 1; height < maxHeightLimit; height++) + { + TESTNOERROR(iTs->GetNearestFontToDesignHeightInPixels((CFont*&)bigFont, TFontSpec(KTypefaceName, height))); + TInt realHeight = bigFont->FontMaxHeight(); + if(realHeight > maxHeight) + { + break; + } + iTs->ReleaseFont(bigFont); + bigFont = NULL; + } + + if (bigFont) + { + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*bigFont, iGlyphCodesLatin, KNumGlyphCodesLatin); + TESTE(iterErr == KErrTooBig, iterErr); + iTs->ReleaseFont(bigFont); + } + else + { + //It is legitimate to fail to create the font, as there are no requirements for the rasterizer here to support such big font. + //In this case we will skip the test. + WARN_PRINTF1(_L("Failed to create font with height greater than 2048")); + } + + __UHEAP_MARKEND; + } + + + + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0640 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + To ensure that the glyph data iterator processes wrong arguments correctly +@SYMTestActions + i. Try to open the glyph data iterator with the negative count passed in + ii. Try to open the glyph data iterator with the positive count and NULL + glyph code array pointer passed in + iii. Try to open the glyph data iterator with a valid glyph code array and + count equal to zero +@SYMTestExpectedResults + At all steps the returned value is set to KErrArgument. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorOpenWithWrongArgument() + { + INFO_PRINTF1(_L("To ensure that the glyph data iterator processes wrong arguments correctly")); + __UHEAP_MARK; + + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*iFont, iGlyphCodesLatin, -1); + TESTE(iterErr == KErrArgument, iterErr); + + iterErr = iter.Open(*iFont, NULL, 1); + TESTE(iterErr == KErrArgument, iterErr); + + iterErr = iter.Open(*iFont, iGlyphCodesLatin, 0); + TESTE(iterErr == KErrArgument, iterErr); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0641 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + To ensure that all allocated RSgImages were released after the + glyph data iterator has been opened and closed multiple times. + +@SYMTestActions + i. Retrieve MSgDriver_Test interface from the SgDriver + ii. Mark alloc start and obtain resorce count from the interface + iii. Iterate through glyph data by calling RFbsGlyphDataIterator::Next() + iv. Retrieve SgImage from the glyph data iterator instance + v. Repeate steps iii and iv multiple times + vi. Release font + vii. Mark alloc end and obtain resorce count from the interface + +@SYMTestExpectedResults + Resorce count at the end matches resorce count at the beginning. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorImageMemoryLeak() + { + __UHEAP_MARK; + + MSgDriver_Test* sgDriverTestInterface = NULL; + TInt err = iSgDriver.GetInterface(sgDriverTestInterface); + if(err != KErrNone) + { + __UHEAP_MARKEND; + WARN_PRINTF2(_L("Failed to obtain MSgDriver_Test interface with error code: %d, the test will be skipped"), err); + return; + } + + TEST(sgDriverTestInterface != NULL); + sgDriverTestInterface->AllocMarkStart(); + + MSgDriver_Profiling* sgDriverProfilInterface = NULL; + err = iSgDriver.GetInterface(sgDriverProfilInterface); + if(err != KErrNone) + { + sgDriverTestInterface->AllocMarkEnd(0); + __UHEAP_MARKEND; + WARN_PRINTF2(_L("Failed to obtain MSgDriver_Profiling interface with error code: %d, the test will be skipped"), err); + return; + } + const TInt resCount = sgDriverProfilInterface->LocalResourceCount(); + + CFbsFont* font = NULL; + err = iTs->GetNearestFontToDesignHeightInPixels((CFont*&)font, TFontSpec(KTypefaceName, 15)); + TESTNOERROR(err); + if(err != KErrNone) + { + __UHEAP_MARKEND; + return; + } + + for (TInt ii = 0; ii < 10; ii++) + { + TInt index = 0; + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*font, iGlyphCodesLatin, KNumGlyphCodesLatin); + TESTNOERROR(iterErr); + for (; (iterErr == KErrNone) && (index < KNumGlyphCodesLatin); iterErr = iter.Next(), ++index) + { + const RSgImage& image = iter.Image(); + } + iter.Close(); + TEST(index == KNumGlyphCodesLatin); + TESTE(iterErr == KErrNotFound, iterErr); + } + iTs->ReleaseFont(font); + const TInt resCountEnd = sgDriverProfilInterface->LocalResourceCount(); + TEST(resCountEnd == resCount); + sgDriverTestInterface->AllocMarkEnd(0); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0659 +@SYMTestPriority Med +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Uses the RFbsGlyphDataIterator to render a large amount of unique glyphs, at a very large + size, to ensure that if graphics memory runs out while the iterator is in use, eviction + takes place and does not corrupt the glyph images in any way. + +@SYMTestActions + i. Create a large CFbsFont from the typeface store (size 100+) + ii. Simulate a low graphics-memory situation by creating enough RSgImages to fill the memory, + releasing one image in order to allow some small amount for the test. + iii. Open a RFbsGlyphDataIterator on the font, using a large array of unique glyph codes. + iv. Iterate through the glyphs, comparing each returned SgImage against the system-memory + representation of the glyph as returned by CFont::GetCharacterData(). + v. Check for errors and mismatches, and release all images created by ii. + +@SYMTestExpectedResults + At each iteration, each glyph should match in size and contents. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorLargeFontStress() + { + INFO_PRINTF1(_L("Stress test using a RFbsGlyphDataIterator with a large font")); +#ifdef __WINS__ + // Cannot run test on emulator reliably - this is because on emulator + // system-memory is used for RSgImages, so using up RSgImage memory may + // cause heap-allocation failures unrelated to the area being tested. + // This test is specifically testing the behaviour when running out of + // RSgImage-based memory (i.e. graphics memory), but on emulator this + // will cause a failed allocation anywhere. + INFO_PRINTF1(_L("Skipping test on emulator...")); +#else + WARN_PRINTF1(_L("---Stress test TO BE REVISITED due to Broadcom defect ESLM-85LDV7 - TB10.1 Closing of RSgImage with duplicate handle used in same thread does not release GPU RAM")); + TEST(EFalse); +/* __UHEAP_MARK; + + const TInt KFontSize = 128; + + CFbsFont* font; + TInt err = iTs->GetNearestFontToDesignHeightInPixels((CFont*&)font, TFontSpec(KMonoTypefaceName, KFontSize)); + TESTNOERROR(err); + // Output the actual fontspec used in the test. + InfoPrintFontSpec(*font); + + // Create 2 buffers for use in comparing SgImages so that we don't run out + // of system memory through allocating memory in the test + TInt maxFontWidth = font->MaxCharWidthInPixels(); + TInt maxFontHeight = font->HeightInPixels(); + iTempBuf1 = (TUint8*) User::AllocZ(maxFontWidth * maxFontHeight); + iTempBuf2 = (TUint8*) User::AllocZ(maxFontWidth * maxFontHeight); + + // In order for the image comparisons to have enough memory to perform, keep + // one large RSgImage which is created before the rest of the graphics memory + // is filled. This image can then be closed before doing the image comparison + // and recreated after the image comparison to ensure that the graphics + // memory is full. Without this image, the image comparison could fail with + // out of memory and the test would fail. + RSgImage tempImage; + TESTNOERROR(tempImage.Create(TSgImageInfo(TSize(1000, 1000), ESgPixelFormatA_8, ESgUsageBitOpenVgImage))); + + TFontSpec actualFontSpec; + actualFontSpec = font->FontSpecInTwips(); + + // Create RSgImages from character data independently from using iterator. + // These will be used for comparing with RSgImages retrieved from iterator. + RArray sgImageFromCharDataArray; + TInt index = 0; + for(; (index < KNumGlyphCodesLatin) && (err == KErrNone); ++index) + { + RSgImage characterDataSgImage; + TInt err = KErrNone; + const TUint8* bitmapData = NULL; + TSize bitmapSize; + TOpenFontCharMetrics metrics; + font->GetCharacterData(iGlyphCodesLatin[index] | KGlyphCodeFlag, metrics, bitmapData, bitmapSize); + + if (bitmapSize != TSize(0, 0)) + { + err = CreateSgImageFromCharacterData(bitmapData, bitmapSize, actualFontSpec.iFontStyle.BitmapType(), characterDataSgImage, iTempBuf1, iTempBuf2); + } + if (KErrNone == err) + { + err = sgImageFromCharDataArray.Append(characterDataSgImage); + } + } + TESTNOERROR(err); + TEST(index == KNumGlyphCodesLatin); + + // Simulate low OOGM situation by creating many RSgImages until out of memory. + RArray sgImageArray; + if (err == KErrNone) + { + TESTNOERROR(NearlyFillGraphicsMemoryWithImages(TSize(256, 256), sgImageArray)); + } + + // Open Iterator on long string of data... + RFbsGlyphDataIterator iter; + TInt iterErr = KErrNone; + if (err == KErrNone) + { + iterErr = iter.Open(*font, iGlyphCodesLatin, KNumGlyphCodesLatin); + TESTNOERROR(iterErr); + } + + // For each glyph, compare it to the system-memory version from GetCharacterData(). + TInt numMismatches = 0; + for(index = 0; (iterErr == KErrNone) && (index < sgImageFromCharDataArray.Count()) && (err == KErrNone); iterErr = iter.Next(), ++index) + { + const TUint8* bitmapData = NULL; + TSize bitmapSize; + TOpenFontCharMetrics metrics; + font->GetCharacterData(iter.GlyphCode() | KGlyphCodeFlag, metrics, bitmapData, bitmapSize); + + if (iter.Rect().Size() == TSize(0, 0)) + { + numMismatches += (bitmapSize != TSize(0, 0)) ? 1 : 0; + } + else + { + // Free up memory so that the image compariso succeeds + // Release all the images used to simulate OOGM. + for (TInt i = sgImageArray.Count() - 1; i >= 0; --i) + { + sgImageArray[i].Close(); + sgImageArray.Remove(i); + } + + TBool match = ETrue; + err = CompareSgImages(iEGL, sgImageFromCharDataArray[index], TRect(bitmapSize), iTempBuf1, iter.Image(), iter.Rect(), iTempBuf2, match); + if (err == KErrNone && !match) + { + ++numMismatches; + } + TInt result = FillGraphicsMemoryWithImages(TSize(256, 256), sgImageArray); + TESTE(result == KErrNoMemory || result == KErrNoGraphicsMemory, result); + } + } + iter.Close(); + + // Release all images created from character data. + for (TInt i = sgImageFromCharDataArray.Count()-1; i >= 0; --i) + { + sgImageFromCharDataArray[i].Close(); + } + sgImageFromCharDataArray.Close(); + + // Release all the images used to simulate OOGM. + for (TInt i = sgImageArray.Count() - 1; i >= 0; --i) + { + sgImageArray[i].Close(); + } + sgImageArray.Close(); + tempImage.Close(); + iTs->ReleaseFont(font); + User::Free(iTempBuf1); + User::Free(iTempBuf2); + iTempBuf1 = NULL; + iTempBuf2 = NULL; + + // Log any errors only after memory is freed - this ensures there is enough + // memory for the logger. + TESTNOERROR(err); + TESTE(iterErr == KErrNotFound, iterErr); + TEST(index == KNumGlyphCodesLatin); + TEST(numMismatches == 0); + + __UHEAP_MARKEND;*/ +#endif + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0660 +@SYMTestPriority Med +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Opens an RFbsGlyphDataIterator on many different fonts of different sizes and typefaces + and uses many fonts, in order to test that the iterator can cope with being used on many + fonts with many glyphs. + +@SYMTestActions + i. Perform test of 100 iterations, where: + 1. A new Latin font is created every iteration in order to force the Typeface Store + to create a brand-new server-side font at each iteration. + 2. For this font, open an RFbsGlyphDataIterator and cycle through all Latin glyphs. + 3. For each glyph, compare against the glyph image returned by CFont::GetCharacterData(). + 4. Keep a record of the number of mismatches, and carry on to next font. + ii. Perform i. again, but using the existing fonts. + iii. Check that there are no mismatches, all glyphs and fonts were successfully checked, + and no error codes returned during the test. + iv. Clean up all resources. + +@SYMTestExpectedResults + The glyphs provided by the iterator should match that returned by GetCharacterData() + for every font and every iteration. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorManyFontsStressL() + { + INFO_PRINTF1(_L("Stress test using a RFbsGlyphDataIterator with hundreds of fonts")); + WARN_PRINTF1(_L("---Stress test TO BE REVISITED due to Broadcom defect ESLM-85LDV7 - TB10.1 Closing of RSgImage with duplicate handle used in same thread does not release GPU RAM")); + TEST(EFalse); + /*__UHEAP_MARK; + + const TInt KNumFonts = 100; + const TInt KNumRepeatsPerFont = 2; + TInt err = KErrNone; + TInt numGlyphMismatches = 0; + + CFbsFont** font = new (ELeave) CFbsFont*[KNumFonts]; + Mem::FillZ(font, sizeof(CFbsFont*) * KNumFonts); + + // Do the whole thing KNumRepeatsPerFont times. The second+ repeats will + // re-use the fonts created in the first repeat, to ensure that fonts that + // may have been evicted are able to be re-used with the iterator. + for (TInt rep = 0; (rep < KNumRepeatsPerFont) && (err == KErrNone); ++rep) + { + // Iterate through all the font variants: + // Iterate all font styles, for all latin typefaces, at increasing sizes. + TInt i = 0; + for (; (i < KNumFonts) && (err == KErrNone); ++i) + { + // Only create this font if this font isn't already valid (i.e. when this is the + // first rep) otherwise re-use it. + if (!font[i]) + { + TFontSpec requestedFontSpec = GenerateDejaVuFontSpec(i); + err = iTs->GetNearestFontToDesignHeightInPixels((CFont*&)font[i], requestedFontSpec); + } + if (err == KErrNone) + { + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*(font[i]), iGlyphCodesLatin, KNumGlyphCodesLatin); + if (iterErr != KErrNone) + { + ERR_PRINTF2(_L("Failed to open RFbsGlyphDataIterator [err=%d]"), iterErr); + InfoPrintFontSpec(*(font[i])); + iStep->SetTestStepResult(EFail); + } + else + { + TInt index = 0; + for(; (iterErr == KErrNone) && (index < KNumGlyphCodesLatin) && (err == KErrNone) ; iterErr = iter.Next(), index++) + { + const TUint8* bitmapData = NULL; + TSize bitmapSize; + TOpenFontCharMetrics metrics; + font[i]->GetCharacterData(iter.GlyphCode() | KGlyphCodeFlag, metrics, bitmapData, bitmapSize); + if (iter.Rect().Size() == TSize(0, 0)) + { + numGlyphMismatches += (bitmapSize != TSize(0, 0)) ? 1 : 0; + } + else + { + TBool match = EFalse; + const TFontSpec fontSpec = font[i]->FontSpecInTwips(); + // Compare to system-memory version of glyph + RSgImage characterDataImage; + err = CreateSgImageFromCharacterData(bitmapData, bitmapSize, fontSpec.iFontStyle.BitmapType(), characterDataImage); + if (err == KErrNone) + { + err = CompareSgImages(iEGL, iter.Image(), iter.Rect(), characterDataImage, TRect(bitmapSize), match); + } + if (err == KErrNone && !match) + { + ++numGlyphMismatches; + } + characterDataImage.Close(); + } + } + iter.Close(); + TESTE(iterErr == KErrNotFound, iterErr); + TEST(index == KNumGlyphCodesLatin); + } + } + } + // Check all the fonts were iterated through. + TEST(i == KNumFonts); + } + TESTNOERROR(err); + TEST(numGlyphMismatches == 0); + + // Cleanup + for (TInt ii = 0; ii < KNumFonts; ii++) + { + iTs->ReleaseFont(font[ii]); + } + delete [] font; + __UHEAP_MARKEND;*/ + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0662 +@SYMTestPriority Low +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Uses a RFbsGlyphDataIterator when there is no graphics memory available in the system. + It shows that when under low graphics memory, Next() returns the correct error code + as per the API (either KErrNoMemory or KErrNoGraphicsMemory, depending on the implementation + of Graphics Resource being used). + +@SYMTestActions + i. Create a CFbsFont from the typeface store. + ii. Simulate a low graphics-memory situation by creating enough RSgImages to fill the memory, + iii. Open a RFbsGlyphDataIterator on the font. + iv. Attempt to use the iterator, calling Next(), checking the returned code. + v. Close the iterator and release all graphics memory from ii. + +@SYMTestExpectedResults + Next() should return either KErrNoMemory or KErrNoGraphicsMemory depending on the implmentation + of Graphics Resource used. It should return the same error as is returned when filling + the graphics memory reaches the limit. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorNoGraphicsMemory() + { + INFO_PRINTF1(_L("Test that when there is no GPU memory available, Next() returns correct error")); + WARN_PRINTF1(_L("---Stress test TO BE REVISITED due to Broadcom defect ESLM-85LDV7 - TB10.1 Closing of RSgImage with duplicate handle used in same thread does not release GPU RAM")); + TEST(EFalse); + /*__UHEAP_MARK; + + const TInt KFontSize = 128; + CFbsFont* font = NULL; + RFbsGlyphDataIterator iter; + + TInt err = iTs->GetNearestFontToDesignHeightInPixels((CFont*&)font, TFontSpec(KMonoTypefaceName, KFontSize)); + TESTNOERROR(err); + + // Simulate low OOGM situation by creating many RSgImages until out of memory. + if (err == KErrNone) + { + InfoPrintFontSpec(*font); + RArray sgImageArray; + TInt iterErr = KErrNone; + TInt gfxMemErr = FillGraphicsMemoryWithImages(TSize(KFontSize, KFontSize), sgImageArray); + TESTE(gfxMemErr == KErrNoMemory || gfxMemErr == KErrNoGraphicsMemory, gfxMemErr); + if (gfxMemErr == KErrNoMemory || gfxMemErr == KErrNoGraphicsMemory) + { + // Next() could either fail with KErrNoMemory or KErrNoGraphicsMemory, but should + // be the same error code as the last attempted creation of an SgImage, done in + // FillGraphicsMemoryWithImages() so compare against that code. + iterErr = iter.Open(*font, iGlyphCodesLatin, KNumGlyphCodesLatin); + for (; iterErr == KErrNone; iterErr = iter.Next()) + { + // no operation + } + iter.Close(); + } + + // Release all the images used to simulate OOGM. + for (TInt i = sgImageArray.Count() - 1; i >= 0; --i) + { + sgImageArray[i].Close(); + } + sgImageArray.Close(); + + // Log any errors only after memory is freed - this ensures there is enough + // memory for the logger. + TESTE(iterErr == gfxMemErr, iterErr); + } + + iTs->ReleaseFont(font); + + __UHEAP_MARKEND;*/ + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0666 +@SYMTestPriority Low +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Uses a RFbsGlyphDataIterator after Next() returns an error, in order to show that + an error does not invalidate the state of the iterator and it is still usable. + +@SYMTestActions + i. Open the RFbsGlyphDataIterator on 1 glyph code. + ii. Store the data of the iterator and call Next() to reach the end of the iterator + iii. Access the glyph data repeatedly and check that the iterator members + still match those in ii. + +@SYMTestExpectedResults + The calls to Next() should cause KErrNotFound since it is past the final glyph. + The iterator data should match at all times since the iterator is never moved. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorNextIsAtomic() + { + INFO_PRINTF1(_L("To ensure that Next() is atomic, if it returns an error it is still useable")); + __UHEAP_MARK; + + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*iFont, iGlyphCodesLatin, 1); + TESTNOERROR(iterErr); + + TSgDrawableId id = iter.Image().Id(); + TOpenFontCharMetrics metrics = iter.Metrics(); + TUint glyphCode = iter.GlyphCode(); + TRect rect = iter.Rect(); + + for (TInt i = 0; i < 2; i++) + { + iterErr = iter.Next(); + TESTE(iterErr == KErrNotFound, iterErr); + + TEST(id == iter.Image().Id()); + TEST(glyphCode == iter.GlyphCode()); + TEST(rect == iter.Rect()); + TEST(CompareMetrics(metrics, iter.Metrics()) == 0); + } + iter.Close(); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0665 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Glyph Atlas white-box test. + To ensure that the same RSgImage is used for repeated requests for the + same glyph in the same call to RFbsGlyphDataIterator:Open(). +@SYMTestActions + i Open the glyph data iterator with a list of glyph codes which are all the same + ii Retrieve the drawable id of each iteration + iii Check that the same drawable id is retrieved in each iteration +@SYMTestExpectedResults + Each iteration returns the same drawable id. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorSameGlyphCodes() + { + INFO_PRINTF1(_L("White box test - Ensure that the same RSgImage is used for repeated requests for the same glyph in the same call to Open()")); + __UHEAP_MARK; + + const TUint KSameRepeatedGlyphCode = DejaVuASCIIToGlyphCode[0x004B]; // 'K' + const TInt KNumGlyphs = 10; + TUint* sameRepeatedGlyphCodes = new TUint[KNumGlyphs]; + for (TInt ii = 0; ii < KNumGlyphs; ++ii) + { + sameRepeatedGlyphCodes[ii] = KSameRepeatedGlyphCode; + } + RFbsGlyphDataIterator iter; + TInt err = iter.Open(*iFont, sameRepeatedGlyphCodes, KNumGlyphs); + + TESTNOERROR(err); + if (KErrNone == err) + { + // get the drawable id of the first glyph and check that the id is valid + TSgDrawableId referenceId = iter.Image().Id(); + RSgImage image; + TESTNOERROR(image.Open(referenceId)); + image.Close(); + TESTNOERROR(iter.Next()); + + for (;KErrNone == err; err = iter.Next()) + { + TEST(referenceId == iter.Image().Id()); + } + TESTE(KErrNotFound == err, err); + } + + iter.Close(); + delete[] sameRepeatedGlyphCodes; + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0668 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + To ensure that the iterator can successfully be opened on an array + of glyph codes of various array sizes. +@SYMTestActions + Perform many iterations of opening an array and cycling through the glyphs, + increasing the size of the array after each iteration. Some simple sanity-checking + of the glyphs is performed. +@SYMTestExpectedResults + KErrNone should be returned at all times. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorManyArraySizes() + { + INFO_PRINTF1(_L("Ensure that the RFbsGlyphDataIterator successfully opens glyph code arrays of many sizes")); + __UHEAP_MARK; + + RFbsGlyphMetricsArray glyphMetricsArray; + + TESTNOERROR(glyphMetricsArray.Get(*iFont, iGlyphCodesLatin, KNumGlyphCodesLatin)); + TInt iterErr = KErrNone; + + for (TInt arraySize = 1; (arraySize < KNumGlyphCodesLatin) && (iterErr == KErrNone); ++arraySize) + { + RFbsGlyphDataIterator iter; + TInt iterErr = iter.Open(*iFont, iGlyphCodesLatin, arraySize); + TESTNOERROR(iterErr); + + for (TInt index = 0; iterErr == KErrNone; iterErr = iter.Next(), ++index) + { + // sanity checking... + if (iter.GlyphCode() != iGlyphCodesLatin[index]) + { + ERR_PRINTF4(_L("Test failed at array size %d - Wanted glyphcode %d, got %d"), arraySize, iGlyphCodesLatin[index], iter.GlyphCode()); + iStep->SetTestStepResult(EFail); + } + if (CompareMetrics(iter.Metrics(), glyphMetricsArray[index]) != 0) + { + ERR_PRINTF3(_L("Test failed at array size %d, metrics check failed at glyphcode %d"), arraySize, iGlyphCodesLatin[index]); + iStep->SetTestStepResult(EFail); + } + } + iter.Close(); + } + + glyphMetricsArray.Close(); + TESTNOERROR(iterErr); + + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0669 +@SYMTestPriority Low +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Negative test case to show that RFbsGlyphDataIterator and RFbsGlyphMetricsArray + do not support bitmap fonts. +@SYMTestActions + i. Load a bitmap font. + ii. Attempt to open an RFbsGlyphDataIterator and RFbsGlyphMetricsArray with the font. +@SYMTestExpectedResults + KErrNotSupported should be returned in both instances. +*/ + void CTFbsGlyphData::TestBitmapFontSupport() + { + INFO_PRINTF1(_L("Test bitmap font not supported")); + __UHEAP_MARK; + + CFbsFont* bitmapFont = NULL; + TInt err = iTs->GetNearestFontToDesignHeightInPixels((CFont*&)bitmapFont, TFontSpec(_L("Digital"), 14)); + TESTNOERROR(err); + TEST(!bitmapFont->IsOpenFont()); + + RFbsGlyphDataIterator iter; + err = iter.Open(*bitmapFont, iGlyphCodesLatin, KNumGlyphCodesLatin); + TEST(err == KErrNotSupported); + iter.Close(); + + RFbsGlyphMetricsArray array; + err = array.Get(*bitmapFont, iGlyphCodesLatin, KNumGlyphCodesLatin); + TEST(err == KErrNotSupported); + array.Close(); + + iTs->ReleaseFont(bitmapFont); + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0671 +@SYMTestPriority High +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Shows that different threads (and therefore RFbsSessions) using fonts with the same + TFontSpec share the same glyphs in the atlas and do not create duplicate entries + in the Glyph Atlas, and that releasing a font clears all associated glyphs in the + atlas. + +@SYMTestActions + i. Create a handle to a test font in the current process. + ii. Spawn a test thread and wait for it to complete. Within the thread : + 1. Create a font with the same fontspec as the parent process. + 2. Use the RFbsGlyphDataIterator API to force rasterization into the glyph atlas. + 3. Release the iterator. + iii. Check there were no leaves from the thread. + iv. Repeat ii. and iii. several times. Before using RFbsGlyphDataIterator, + the thread checks that the glyphs are still in the atlas from the first thread. + v. Check that the number of fonts in the atlas has increased by one only. + vi. Check that the number of glyphs in the atlas has increased by the size of the + glyph code array. + vii. Release the font in the parent process, thereby releasing the font and glyphs + in the glyph atlas, and check that the state of the atlas is the same as before + the test is run. + +@SYMTestExpectedResults + All threads should return no errors or leaves or panics. + After all threads have finished: + The glyph count should have increased by the size of the glyph code array used + in the RFbsGlyphDataIterator, showing that glyphs are only being added to the atlas + once. The number of fonts in the atlas should have increased by one, showing + that only the single underlying font object is being added to the atlas, despite + different RFbsSessions and CFbsFont instances used. + After the test font is released in the main process: + The glyph count and font count return to the pre-test value, showing that when + the last handle to a TFontSpec is released, the atlas frees its associated data. +*/ +void CTFbsGlyphData::TestMultithreadShareSingleFont() + { + INFO_PRINTF1(_L("Test glyphs shared between RFbsSessions/threads/processes")); +#ifndef _DEBUG + // Test relies on debug-only FBS messages EFbsMessAtlasGlyphCount and EFbsMessAtlasFontCount + INFO_PRINTF1(_L("Skipping test in release mode")); +#else + __UHEAP_MARK; + + _LIT(KThreadName, "GlyphDataTestThread"); + const TInt KNumTestThreads = 5; + const TFontSpec KTestFontSpec(KTypefaceName, 50); + const TInt atlasFontCountStart = iFbs->SendCommand(EFbsMessAtlasFontCount); + const TInt atlasGlyphCountStart = iFbs->SendCommand(EFbsMessAtlasGlyphCount); + + CFbsFont* testFont; + TInt err = iTs->GetNearestFontToDesignHeightInPixels( (CFont*&)testFont, KTestFontSpec); + if (err != KErrNone) + { + ERR_PRINTF2(_L("Could not load font, err = %d"), err); + iStep->SetTestStepResult(EFail); + return; + } + + // Check there are no glyphs belonging to the test font before the test starts. + TInt atlasFontGlyphCount = iFbs->SendCommand(EFbsMessAtlasGlyphCount, testFont->Handle()); + TEST(atlasFontGlyphCount == 0); + + TGlyphDataMultithreadParams params = {KTestFontSpec, iGlyphCodesLatin, KNumGlyphCodesLatin, NULL}; + + // Run the test threads sequentially, and check its exit status. + RThread testThread; + TInt numThreadsPassed = 0; + for (TInt i = 0; i < KNumTestThreads; i++) + { + TBool threadPassed = ETrue; + TGlyphDataThreadInfo info = {EGlyphDataMultiSessionTestShareGlyphs, params, i, iStep}; + err = testThread.Create(KThreadName, CTFbsGlyphData::ThreadFunction, KDefaultStackSize, KTestThreadMinHeapSize, KTestThreadMaxHeapSize, &info); + TESTNOERROR(err); + + TRequestStatus statusThread; + testThread.Logon(statusThread); + testThread.Resume(); + + User::WaitForRequest(statusThread); + TInt threadResult = testThread.ExitReason(); + if (threadResult != KErrNone) + { + ERR_PRINTF3(_L("Thread %i: Terminated with reason %d"), i, threadResult); + threadPassed = EFalse; + } + TExitCategoryName exitCategory = testThread.ExitCategory(); + if (exitCategory.Compare(_L("Kill")) != 0) + { + ERR_PRINTF3(_L("Thread %i: Terminated with reason category '%S'"), i, &exitCategory); + threadPassed = EFalse; + } + testThread.Close(); + numThreadsPassed += (threadPassed) ? 1 : 0; + } + TEST(numThreadsPassed == KNumTestThreads); + + // Check that the atlas still contains the glyphs and the font created by the threads + // after they have died, since the font is still open in this process. + atlasFontGlyphCount = iFbs->SendCommand(EFbsMessAtlasGlyphCount, testFont->Handle()); + TEST(atlasFontGlyphCount == params.iGlyphCodesCount); + TInt atlasFontCount = iFbs->SendCommand(EFbsMessAtlasFontCount); + TEST(atlasFontCount == (atlasFontCountStart + 1)); + + iTs->ReleaseFont(testFont); + testFont = NULL; + + // Check the atlas state is now the same as it was before the test started, + // now that the last remaining handle to the font used in the threads is released. + TInt atlasGlyphCountEnd = iFbs->SendCommand(EFbsMessAtlasGlyphCount); + TEST(atlasGlyphCountStart == atlasGlyphCountEnd); + TInt atlasFontCountEnd = iFbs->SendCommand(EFbsMessAtlasFontCount); + TEST(atlasFontCountStart == atlasFontCountEnd); + __UHEAP_MARKEND; +#endif + } + +/** +Worker thread for TestMultithreadShareSingleFont(). +The thread uses RFbsGlyphDataIterator on a CFbsFont of the given TFontSpec. +Once complete the atlas is queried for the number of associated glyphs. + */ +void CTFbsGlyphData::ThreadShareGlyphsL(TInt aThreadNum, TGlyphDataMultithreadParams& aParam, CTestStep* aStep) + { + User::LeaveIfError(RFbsSession::Connect()); + CFbsTypefaceStore* ts = CFbsTypefaceStore::NewL(NULL); + CleanupStack::PushL(ts); + RFbsSession* fbs = RFbsSession::GetSession(); + + CFbsFont* font; + TInt err = ts->GetNearestFontToDesignHeightInPixels((CFont*&)font, aParam.iFontSpec); + User::LeaveIfError(err); + + if (aThreadNum > 0) + { + // If this is not the first thread, it means the first thread has already executed and + // populated the glyph atlas with the glyphs. The font created by this thread + // should already have its glyphs in the atlas. + TInt fontGlyphCount = fbs->SendCommand(EFbsMessAtlasGlyphCount, font->Handle()); + if (fontGlyphCount != aParam.iGlyphCodesCount) + { + aStep->ERR_PRINTF4(_L("Thread %d: Only %d glyphs in atlas before first iteration, expected %d"), aThreadNum, fontGlyphCount, aParam.iGlyphCodesCount); + aStep->SetTestStepResult(EFail); + } + } + + RFbsGlyphDataIterator iter; + for (err = iter.Open(*font, aParam.iGlyphCodes, aParam.iGlyphCodesCount); err == KErrNone; err = iter.Next()) + { + // no-op + } + iter.Close(); + + // Check that the glyphs of this font have been added to the atlas + TInt fontGlyphCount = fbs->SendCommand(EFbsMessAtlasGlyphCount, font->Handle()); + if (fontGlyphCount != aParam.iGlyphCodesCount) + { + aStep->ERR_PRINTF5(_L("Thread %d: Only %d glyphs in atlas after last iteration, expected %d (err=%d)"), aThreadNum, fontGlyphCount, aParam.iGlyphCodesCount, err); + aStep->SetTestStepResult(EFail); + } + if (err != KErrNotFound) + { + aStep->ERR_PRINTF3(_L("Thread %d: Error during test = %d"), aThreadNum, err); + aStep->SetTestStepResult(EFail); + } + + ts->ReleaseFont(font); + CleanupStack::PopAndDestroy(1); // ts + RFbsSession::Disconnect(); + } + + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0672 +@SYMTestPriority Medium +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Tests that with many concurrent sessions connected to Fbserv, the atlas successfully + returns the correct glyph images even if the atlas becomes full and has to evict glyphs. + +@SYMTestActions + i. Create 25 threads, each a unique session with Fbserv. + ii. Launch the threads simultaneously. In each thread: + 1. Create a FBS typeface store and create a font which is unique in the process. + 2. Use RFbsGlyphDataIterator to iterate through the latin glyph codes. + 3. Check the image is correct for each glyph against the image returned by + CFont::GetCharacterData(). + 4. Close the iterator. + 5. Release the font and close the typeface store. + iii. Once all threads have finished, check the exit status of each thread + +@SYMTestExpectedResults + Every glyph for every thread should match the image returned by GetCharacterData() + All threads should exit normally with no Leave code. +*/ +_LIT(KTestMultithreadStressFinishSemaphore, "TestMultithreadStressAtlasFinish"); + +void CTFbsGlyphData::TestMultithreadStressAtlas() + { + INFO_PRINTF1(_L("Stress test glyph atlas with multiple RFbsSessions")); + WARN_PRINTF1(_L("---Stress test TO BE REVISITED due to Broadcom defect ESLM-85NEFT - TB10.1 eglCreateImageKHR hangs during multithreading")); + TEST(EFalse); + /*__UHEAP_MARK; + + TInt err = KErrNone; + const TInt KNumTestThreads = 25; + _LIT(KThreadNameFormat, "GlyphDataTestThread%i"); + + // Create a semaphore that is signalled by each test thread when it has finished. + RSemaphore threadFinishSemaphore; + err = threadFinishSemaphore.CreateGlobal(KTestMultithreadStressFinishSemaphore, 0, EOwnerThread); + TESTNOERROR(err); + + // Prepare the testdata for the threads + // Each thread will have a TFontSpec which will cause unique CFbsFonts + // to be created in the server, and therefore the atlas. + RThread testThread[KNumTestThreads]; + TGlyphDataThreadInfo testInfo[KNumTestThreads]; + for (TInt i = 0; i < KNumTestThreads; ++i) + { + testInfo[i].iStep = iStep; + testInfo[i].iTest = EGlyphDataMultiSessionTestStressAtlas; + testInfo[i].iParams.iFontSpec = GenerateDejaVuFontSpec(i); + testInfo[i].iParams.iGlyphCodes = iGlyphCodesLatin; + testInfo[i].iParams.iGlyphCodesCount = KNumGlyphCodesLatin; + testInfo[i].iParams.iEGL = iEGL; + testInfo[i].iThreadNum = i; + TBuf<128> threadName; + threadName.AppendFormat(KThreadNameFormat, i); + err = testThread[i].Create(threadName, CTFbsGlyphData::ThreadFunction, KDefaultStackSize, KTestThreadMinHeapSize, KTestThreadMaxHeapSize, &testInfo[i]); + TESTNOERROR(err); + } + + // All threads are created, start them simultaneously. + for (TInt i = 0; i < KNumTestThreads; ++i) + { + testThread[i].Resume(); + } + // Wait for all threads to finish. + for (TInt i = 0; i < KNumTestThreads; ++i) + { + threadFinishSemaphore.Wait(); + } + // Allow some time for remaining threads to finish tidy-up. + User::After(100000); + threadFinishSemaphore.Close(); + + TInt numThreadsPassed = 0; + for (TInt i = 0; i < KNumTestThreads; ++i) + { + TBool threadPassed = ETrue; + TInt threadResult = testThread[i].ExitReason(); + if (threadResult != KErrNone) + { + ERR_PRINTF3(_L("Thread %i: Terminated with reason %d"), i, threadResult); + threadPassed = EFalse; + } + TExitCategoryName exitCategory = testThread[i].ExitCategory(); + if (exitCategory.Compare(_L("Kill")) != 0) + { + ERR_PRINTF3(_L("Thread %i: Terminated with reason category '%S'"), i, &exitCategory); + threadPassed = EFalse; + } + testThread[i].Close(); + numThreadsPassed += (threadPassed) ? 1 : 0; + } + TEST(numThreadsPassed == KNumTestThreads); + + __UHEAP_MARKEND;*/ + } +/** +Worker thread for TestMultithreadStressAtlas(). +The thread uses RFbsGlyphDataIterator on a CFbsFont of the given TFontSpec. +For each glyph, the image returned by the iterator is compared to the image +returned from CFont::GetCharacterData(). +Once complete, the semaphore is signalled to tell the parent process it has +finished. + */ +void CleanupFinishSemaphore(TAny* aItem) + { + RSemaphore* semaphore = reinterpret_cast(aItem); + semaphore->Signal(); + semaphore->Close(); + } +void CTFbsGlyphData::ThreadStressAtlasL(TInt aThreadNum, TGlyphDataMultithreadParams& aParam, CTestStep* aStep) + { + TOpenFontCharMetrics charMetrics; + const TUint8* bitmapData; + TSize bitmapSize; + RSgImage charDataImage; + + RSemaphore threadFinishSemaphore; + User::LeaveIfError(threadFinishSemaphore.OpenGlobal(KTestMultithreadStressFinishSemaphore)); + CleanupStack::PushL(TCleanupItem(CleanupFinishSemaphore, &threadFinishSemaphore)); + + User::LeaveIfError(RFbsSession::Connect()); + CFbsTypefaceStore* ts = CFbsTypefaceStore::NewL(NULL); + CleanupStack::PushL(ts); + + CFbsFont* font; + User::LeaveIfError(ts->GetNearestFontToDesignHeightInPixels((CFont*&)font, aParam.iFontSpec)); + + TInt numGlyphMatches = 0; + TInt index = 0; + TInt err = KErrNone; + RFbsGlyphDataIterator iter; + for (err = iter.Open(*font, aParam.iGlyphCodes, aParam.iGlyphCodesCount); err == KErrNone; err = iter.Next(), ++index) + { + TBool glyphMatch = EFalse; + font->GetCharacterData(aParam.iGlyphCodes[index] | KGlyphCodeFlag, charMetrics, bitmapData, bitmapSize); + if (bitmapSize == TSize(0, 0)) + { + glyphMatch = (bitmapSize == iter.Rect().Size()); + } + else + { + err = CreateSgImageFromCharacterData(bitmapData, bitmapSize, font->FontSpecInTwips().iFontStyle.BitmapType(), charDataImage); + if (err == KErrNone) + { + err = CompareSgImages(aParam.iEGL, iter.Image(), iter.Rect(), charDataImage, TRect(bitmapSize), glyphMatch); + } + charDataImage.Close(); + } + if (err != KErrNone) + { + break; + } + numGlyphMatches += (glyphMatch) ? 1 : 0; + } + iter.Close(); + + if (index != aParam.iGlyphCodesCount) + { + aStep->ERR_PRINTF5(_L("Thread %d: Iterator terminated early - %d out of %d glyphs (err=%d)"), aThreadNum, index, aParam.iGlyphCodesCount, err); + aStep->SetTestStepResult(EFail); + } + if (index != numGlyphMatches) + { + aStep->ERR_PRINTF4(_L("Thread %d: Matched %d out of %d glyphs"), aThreadNum, numGlyphMatches, aParam.iGlyphCodesCount); + aStep->SetTestStepResult(EFail); + } + + ts->ReleaseFont(font); + CleanupStack::PopAndDestroy(2); // ts, threadFinishSemaphore + RFbsSession::Disconnect(); + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0673 +@SYMTestPriority Medium +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Tests the robustness of using RFbsGlyphMetricsArray when the client heap and the + FbServ private heap experience failures allocating memory, causing no panics + or leaves. + +@SYMTestActions + i. Set the default heap failure for the next heap allocation. + ii. Create a new CFbsFont using a TFontSpec not already in the glyph atlas. + iii. Call RFbsGlyphMetricsArray::Get(), and close the array. + iv. Release the font so that nothing is left in the cache as a result of + attempting to use it, and reset the heap failure state. + v. While iii returns KErrNoMemory, increment the failure count and repeat + step ii. + vi. Using a separate font so that the test is not affected by the earlier + run, repeat ii. to v., but rather than setting the default heap to + fail, the FbServ private heap is set to fail, via IPC messages to Fbs. + +@SYMTestExpectedResults + If no errors occur, KErrNone should be returned after a certain number of + repetitions. Any other error code denotes a problem handling low-memory + situtations. +*/ +void CTFbsGlyphData::TestGlyphMetricsArrayHeapOOML() + { + INFO_PRINTF1(_L("Test RFbsGlyphMetricsArray during heap alloc failure")); + __UHEAP_MARK; + + // Create a font that wont be in the cache already... + TInt rep = 0; + TInt err = KErrNoMemory; + CFbsFont* font = NULL; + + while (err == KErrNoMemory) + { + User::LeaveIfError(iTs->GetNearestFontInPixels((CFont*&)font, GenerateDejaVuFontSpec(10))); + __UHEAP_FAILNEXT(rep); + RFbsGlyphMetricsArray array; + err = array.Get(*font, iGlyphCodesLatin, KNumGlyphCodesLatin); + array.Close(); + __UHEAP_RESET; + iTs->ReleaseFont(font); + font = NULL; + ++rep; + } + + TESTE(err == KErrNone, err); + if (err == KErrNone) + { + INFO_PRINTF2(_L("Client Heap OOM : Test passed after rep %d"), rep); + } + else + { + ERR_PRINTF3(_L("Client Heap OOM : Test failed with err=%d, after rep %d"), err, rep); + } + + // Now test when the server-side FbServ heap fails... + rep = 0; + err = KErrNoMemory; + + while (err == KErrNoMemory) + { + User::LeaveIfError(iTs->GetNearestFontInPixels((CFont*&)font, GenerateDejaVuFontSpec(11))); + iFbs->SendCommand(EFbsMessSetHeapFail, RFbsSession::EHeapFailTypeServerMemory, rep); + RFbsGlyphMetricsArray array; + err = array.Get(*font, iGlyphCodesLatin, KNumGlyphCodesLatin); + array.Close(); + iFbs->SendCommand(EFbsMessSetHeapReset, RFbsSession::EHeapFailTypeServerMemory); + iTs->ReleaseFont(font); + font = NULL; + ++rep; + } + + TESTE(err == KErrNone, err); + if (err == KErrNone) + { + INFO_PRINTF2(_L("FBServ Heap OOM : Test passed after rep %d"), rep); + } + else + { + ERR_PRINTF3(_L("FBServ Heap OOM : Test failed with err=%d, after rep %d"), err, rep); + } + __UHEAP_MARKEND; + } + +/** +@SYMTestCaseID GRAPHICS-FBSERV-0674 +@SYMTestPriority Medium +@SYMTestType UT +@SYMTestStatus Implemented +@SYMPREQ PREQ2678 + +@SYMTestCaseDesc + Tests the robustness of using RFbsGlyphDataIterator when the client heap and the + FbServ private heap experience failures allocating memory, causing no panics + or leaves. + +@SYMTestActions + i. Set the default heap failure for the next heap allocation. + ii. Create a new CFbsFont using a TFontSpec not already in the glyph atlas. + iii. Call RFbsGlyphDataIterator::Open(), and close the array. + iv. Release the font so that nothing is left in the cache as a result of + attempting to use it, and reset the heap failure state. + v. While iii returns KErrNoMemory, increment the failure count and repeat + step ii. + vi. Using a separate font so that the test is not affected by the earlier + run, repeat ii. to v., but rather than setting the default heap to + fail, the FbServ private heap is set to fail, via IPC messages to Fbs. + +@SYMTestExpectedResults + If no errors occur, KErrNone should be returned after a certain number of + repetitions. Any other error code denotes a problem handling low-memory + situtations. +*/ +void CTFbsGlyphData::TestGlyphDataIteratorHeapOOML() + { + INFO_PRINTF1(_L("Test RFbsGlyphDataIterator during heap alloc failure")); + __UHEAP_MARK; + + // Create a font that wont be in the cache already... + TInt rep = 0; + TInt err = KErrNoMemory; + CFbsFont* font = NULL; + + while (err == KErrNoMemory) + { + User::LeaveIfError(iTs->GetNearestFontInPixels((CFont*&)font, GenerateDejaVuFontSpec(10))); + __UHEAP_FAILNEXT(rep); + RFbsGlyphDataIterator iter; + err = iter.Open(*font, iGlyphCodesLatin, KNumGlyphCodesLatin); + if (err == KErrNone) + { + while (err == KErrNone) + { + err = iter.Next(); + } + err = (err == KErrNotFound) ? KErrNone : err; + } + iter.Close(); + __UHEAP_RESET; + iTs->ReleaseFont(font); + font = NULL; + ++rep; + } + + TESTE(err == KErrNone, err); + if (err == KErrNone) + { + INFO_PRINTF2(_L("Client Heap OOM : Test passed after rep %d"), rep); + } + else + { + ERR_PRINTF3(_L("Client Heap OOM : Test failed with err=%d, after rep %d"), err, rep); + } + + // Now test when the server-side FbServ heap fails... + rep = 0; + err = KErrNoMemory; + + while (err == KErrNoMemory) + { + User::LeaveIfError(iTs->GetNearestFontInPixels((CFont*&)font, GenerateDejaVuFontSpec(11))); + iFbs->SendCommand(EFbsMessSetHeapFail, RFbsSession::EHeapFailTypeServerMemory, rep); + RFbsGlyphDataIterator iter; + err = iter.Open(*font, iGlyphCodesLatin, KNumGlyphCodesLatin); + if (err == KErrNone) + { + while (err == KErrNone) + { + err = iter.Next(); + } + err = (err == KErrNotFound) ? KErrNone : err; + } + iter.Close(); + iFbs->SendCommand(EFbsMessSetHeapReset, RFbsSession::EHeapFailTypeServerMemory); + iTs->ReleaseFont(font); + font = NULL; + ++rep; + } + + TESTE(err == KErrNone, err); + if (err == KErrNone) + { + INFO_PRINTF2(_L("FBServ Heap OOM : Test passed after rep %d"), rep); + } + else + { + ERR_PRINTF3(_L("FBServ Heap OOM : Test failed with err=%d, after rep %d"), err, rep); + } + __UHEAP_MARKEND; + } + +/** +Utility function. Prints out a description of the font's fontspec to the log. + */ +void CTFbsGlyphData::InfoPrintFontSpec(const CFont& aFont) + { + _LIT(KMonochromeBitmap, "Mono"); + _LIT(KAntiAliasedBitmap, "AA"); + _LIT(KStyleItalic, "Italic"); + _LIT(KStyleBold, "Bold"); + _LIT(KStyleNormal, "Normal"); + _LIT(KUnknown, "Unknown"); + TBufC<9> bitmapType; + TBuf<12> fontStyle; + TFontSpec fontSpec = aFont.FontSpecInTwips(); + switch(fontSpec.iFontStyle.BitmapType()) + { + case EMonochromeGlyphBitmap: + bitmapType = KMonochromeBitmap; + break; + case EAntiAliasedGlyphBitmap: + bitmapType = KAntiAliasedBitmap; + break; + default: + bitmapType = KUnknown; + } + + if (fontSpec.iFontStyle.StrokeWeight() == EStrokeWeightBold) + { + fontStyle.Append(KStyleBold); + } + if (fontSpec.iFontStyle.Posture() == EPostureItalic) + { + fontStyle.Append(KStyleItalic); + } + if (fontStyle.Length() == 0) + { + fontStyle = KStyleNormal; + } + + INFO_PRINTF5(_L("Font: name=%S size=%dtw type=%S style=%S"), &(fontSpec.iTypeface.iName), fontSpec.iHeight, &bitmapType, &fontStyle); + } + + +/** +Static utility function. Performs a per-pixel comparison of two open RSgImages. +To do this requires access to the binary data of the images, only accessable +via EGL and Khronos APIs. This function will bind the RSgImages to VGImages +and uses OpenVG to retrieve the image data in 8bpp. +@param aEGL An EGL Helper to read the SgImages into system memory. +@param aImageA The first image to compare. +@param aRectA A rectangular portion in pixels of the first image to compare. +@param aImageB The second image to compare. +@param aRectB A rectangular portion in pixels fo the second image to compare. +@param aMatch A boolean value, which on return tells the caller whether the two + images were deemed to match. +@return KErrNone, if the comparison took place, otherwise one of the system-wide + error codes. +*/ +TInt CTFbsGlyphData::CompareSgImages(CEGLHelper* aEGL, const RSgImage& aImageA, const TRect& aRectA, const RSgImage& aImageB, const TRect& aRectB, TBool& aMatch) + { + return CTFbsGlyphData::CompareSgImages(aEGL, aImageA, aRectA, NULL, aImageB, aRectB, NULL, aMatch); + } + +/** +Static utility function. Performs a per-pixel comparison of two open RSgImages. +To do this requires access to the binary data of the images, only accessable +via EGL and Khronos APIs. This function will bind the RSgImages to VGImages +and uses OpenVG to retrieve the image data in 8bpp. +This version allows pre-created memory to be used in the comparison, to avoid +allocation failure in low memory testing. +@param aEGL An EGL Helper to read the SgImages into system memory buffers. +@param aImageA The first image to compare. +@param aRectA A rectangular portion in pixels of the first image to compare. +@param aBufferA If non-NULL, specifies a memory buffer to read the data of + aImageA into, otherwise a buffer is dynamically allocated. +@param aImageB The second image to compare. +@param aRectB A rectangular portion in pixels fo the second image to compare. +@param aBufferB If non-NULL, specifies a memory buffer to read the data of + aImageB into, otherwise a buffer is dynamically allocated. +@param aMatch A boolean value, which on return tells the caller whether the two + images were deemed to match. +@return KErrNone, if the comparison took place, otherwise one of the system-wide + error codes. +*/ +TInt CTFbsGlyphData::CompareSgImages(CEGLHelper* aEGL, const RSgImage& aImageA, const TRect& aRectA, TUint8* aBufferA, const RSgImage& aImageB, const TRect& aRectB, TUint8* aBufferB, TBool& aMatch) + { + // By default, assume they do not match. + aMatch = EFalse; + +#ifdef SAVEGLYPHSTOMBMDURINGCOMPARISON + + static TInt countToAppend = 0; + + CFbsBitmap* bitmap = NULL; + if (KErrNone == CreateBitmapFromSgImage(aEGL, aImageA, aRectA, bitmap)) + { + TBuf buf; + buf.AppendNum( countToAppend ); + TPtrC nameAppend( buf ); + + SaveBmp(bitmap, &nameAppend, EFalse); + } + delete bitmap; + if (KErrNone == CreateBitmapFromSgImage(aEGL, aImageB, aRectB, bitmap)) + { + TBuf buf; + buf.AppendNum( countToAppend ); + TPtrC nameAppend( buf ); + + SaveBmp(bitmap, &nameAppend, ETrue); + } + delete bitmap; + + countToAppend++; + +#endif // SAVEGLYPHSTOMBMDURINGCOMPARISON + + TSgImageInfo imageInfoA; + TSgImageInfo imageInfoB; + if (aImageA.GetInfo(imageInfoA) != KErrNone || + aImageB.GetInfo(imageInfoB) != KErrNone) + { + return KErrBadHandle; + } + + // Check the sizes of the images match, and the rects reside on the images. + if (aRectA.Size() != aRectB.Size() || + !TRect(imageInfoA.iSizeInPixels).Intersects(aRectA) || + !TRect(imageInfoB.iSizeInPixels).Intersects(aRectB)) + { + return KErrNone; + } + const TSize KBufferSize = aRectA.Size(); + const TInt KDataStride = KBufferSize.iWidth; + + TBool freeTempBufA = EFalse; + TBool freeTempBufB = EFalse; + if (!aBufferA) + { + aBufferA = (TUint8*) User::AllocZ(KDataStride * KBufferSize.iHeight); + freeTempBufA = ETrue; + } + if (!aBufferA) + { + return KErrNoMemory; + } + TInt err = aEGL->GetSgImageData(aImageA, aRectA, aBufferA); + if (err != KErrNone) + { + if (freeTempBufA) + { + User::Free(aBufferA); + aBufferA = NULL; + } + return err; + } + if (!aBufferB) + { + aBufferB = (TUint8*) User::AllocZ(KDataStride * KBufferSize.iHeight); + freeTempBufB = ETrue; + } + if (!aBufferB) + { + if (freeTempBufA) + { + User::Free(aBufferA); + aBufferA = NULL; + } + return KErrNoMemory; + } + err = aEGL->GetSgImageData(aImageB, aRectB, aBufferB); + if (err != KErrNone) + { + if (freeTempBufA) + { + User::Free(aBufferA); + aBufferA = NULL; + } + if (freeTempBufB) + { + User::Free(aBufferB); + aBufferB = NULL; + } + return err; + } + + // Perform a per-pixel comparison, scanline by scanline. + // The loop will break as soon as a mismatch is detected. + aMatch = ETrue; + for (TInt scanline = 0; (scanline < KBufferSize.iHeight) && aMatch; ++scanline) + { + TUint8* scanlineImageA = aBufferA + (scanline * KDataStride); + TUint8* scanlineImageB = aBufferB + (scanline * KDataStride); + aMatch = (Mem::Compare(scanlineImageA, KBufferSize.iWidth, scanlineImageB, KBufferSize.iWidth) == 0); + } + + if (freeTempBufA) + { + User::Free(aBufferA); + aBufferA = NULL; + } + if (freeTempBufB) + { + User::Free(aBufferB); + aBufferB = NULL; + } + + return KErrNone; + } + +/** +Second thread entry function for multi-threaded tests. +*/ +TInt CTFbsGlyphData::ThreadFunction(TAny* aParam) + { + __UHEAP_MARK; + CTrapCleanup* cleanupStack = CTrapCleanup::New(); + if (!cleanupStack) + { + return KErrNoMemory; + } + + TGlyphDataThreadInfo* info = static_cast(aParam); + TRAPD(result, + switch(info->iTest) + { + case EGlyphDataMultiSessionTestShareGlyphs: + CTFbsGlyphData::ThreadShareGlyphsL(info->iThreadNum, info->iParams, info->iStep); + break; + case EGlyphDataMultiSessionTestStressAtlas: + CTFbsGlyphData::ThreadStressAtlasL(info->iThreadNum, info->iParams, info->iStep); + break; + default: + User::Leave(KErrArgument); + } + ); + + delete cleanupStack; + __UHEAP_MARKEND; + return result; + } + + + +/* + ----------------------------------------- + Static utility Methods used by the tests. + ----------------------------------------- +*/ + + + +/** +Utility method that fills the RSgImage memory with RSgImages until either KErrNoMemory +or KErrNoGraphicsMemory is returned. + +@param aSize The size of the image used to fill the graphics memory - a form of granularity +@param aImages Returns the array of the images used to fill the graphics memory. +@return KErrNoGraphicsMemory or KErrNoMemory if successful, otherwise one of the system + wide error codes. + */ +/*static TInt FillGraphicsMemoryWithImages(const TSize& aSize, RArray& aImages) + { + TInt err = KErrNone; + while (KErrNone == err) + { + RSgImage sgImage; + err = sgImage.Create(TSgImageInfo(aSize, ESgPixelFormatA_8, ESgUsageBitOpenVgImage)); + if (KErrNone == err) + { + err = aImages.Append(sgImage); + } + } + return err; + }*/ + +/** +Utility method that fills the RSgImage memory with RSgImages until either KErrNoMemory +or KErrNoGraphicsMemory is returned and then closes one RSgImage to free up some memory. + +@param aSize The size of the image used to fill the graphics memory - a form of granularity +@param aImages Returns the array of the images used to fill the graphics memory. +@return KErrNone if successful, otherwise one of the system wide error codes. + */ +/*static TInt NearlyFillGraphicsMemoryWithImages(const TSize& aSize, RArray& aImages) + { + TInt err = FillGraphicsMemoryWithImages(aSize, aImages); + if (err == KErrNoMemory || err == KErrNoGraphicsMemory) + { + if (aImages.Count() > 0) + { + // Remove an image to free up some memory. + TInt lastIndex = aImages.Count() - 1; + aImages[lastIndex].Close(); + aImages.Remove(lastIndex); + } + err = KErrNone; + } + return err; + }*/ + +/** +Static utility function. Creates an 8bpp RSgImage from 1bpp or 8bpp character +data, with VGImage usage flag set. +@param aData The character image data. Either in 8bpp or 1bpp RLE format. +@param aSize The size of the character image in pixels. +@param aType The type of glyph - Monochrome or Antialiased. +@param aSgImage A closed image which will be populated with 8bpp image data. +*/ +static TInt CreateSgImageFromCharacterData(const TUint8* aData, const TSize& aSize, TGlyphBitmapType aType, RSgImage& aImage) + { + return CreateSgImageFromCharacterData(aData, aSize, aType, aImage, NULL, NULL); + } + +/** +Static utility function. Creates an 8bpp RSgImage from 1bpp or 8bpp character +data, with VGImage usage flag set. +This overload allows the memory for the buffers to be pre-created to avoid +memory allocation failure during low-memory testing. +@param aData The character image data. Either in 8bpp or 1bpp RLE format. +@param aSize The size of the character image in pixels. +@param aType The type of glyph - Monochrome or Antialiased. +@param aSgImage A closed image which will be populated with 8bpp image data. +@param aBuffer1 If non-NULL, used as a memory buffer for reading the decoded + image data into for monochrome images. +@param aBuffer2 If non-NULL, used as a memory buffer for the decoded image + data for monochrome images. +*/ +static TInt CreateSgImageFromCharacterData(const TUint8* aData, const TSize& aSize, TGlyphBitmapType aType, RSgImage& aImage, TUint8* aBuffer1, TUint8* aBuffer2) + { + TInt err = KErrNone; + if (aSize == TSize(0, 0)) + { + return KErrArgument; + } + TUint8* dataBuf = NULL; + TInt dataStride = 0; + TBool freeDataBuf = EFalse; + if (aType == EAntiAliasedGlyphBitmap) + { + dataBuf = const_cast(aData); + dataStride = aSize.iWidth; + } + else if (aType == EMonochromeGlyphBitmap) + { + TUint8* binaryData = NULL; + TUint8* tempBuf = NULL; + TInt binaryDataStride = ((aSize.iWidth + 31) / 32) << 2; + TInt binaryDataSize = binaryDataStride * aSize.iHeight; + if (aBuffer1 && User::AllocLen(aBuffer1) >= binaryDataSize) + { + binaryData = aBuffer1; + } + else + { + tempBuf = (TUint8*) User::AllocZ(binaryDataSize); + if (!tempBuf) + { + return KErrNoMemory; + } + binaryData = tempBuf; + } + // Unpack the run length encoded data into 1bpp + DecodeBinaryData(aSize, aData, binaryDataStride, reinterpret_cast(binaryData)); + dataStride = aSize.iWidth; + TInt byteDataSize = dataStride * aSize.iHeight; + TUint8* byteData = NULL; + // If aByteBuf supplied, use that instead of allocating a new buffer here. + if (aBuffer2 && User::AllocLen(aBuffer2) >= byteDataSize) + { + byteData = aBuffer2; + } + else + { + byteData = (TUint8*) User::AllocZ(byteDataSize); + if (!byteData) + { + User::Free(tempBuf); + return KErrNoMemory; + } + freeDataBuf = ETrue; + } + dataBuf = byteData; + for (TInt scanline = 0; scanline < aSize.iHeight; ++scanline) + { + TUint8* srcByte = binaryData; + for (TInt pixel = 0; pixel < aSize.iWidth; pixel++) + { + *(byteData+pixel) = ((*srcByte & (1 << (pixel % 8))) == 0) ? 0 : 0xFF; + if (((pixel + 1) % 8) == 0) srcByte++; + } + byteData += dataStride; + binaryData += binaryDataStride; + } + User::Free(tempBuf); + } + else + { + return KErrArgument; + } + + // Create RSgImage from CFbsBitmap. + TSgImageInfo sgImageInfo(aSize, ESgPixelFormatA_8, ESgUsageBitOpenVgImage); + err = aImage.Create(sgImageInfo, dataBuf, dataStride); + if (freeDataBuf) + { + User::Free(dataBuf); + } + return err; + } + + +/** +Static utility function, Copies image data line(s) to a destination. +@param aBinaryDataPtr pointer to a destination buffer. +@param aBufferWords Stride of the image. +@param aData Pointer to a source buffer. +@param aBitShift Number of bits, binary data will be shifted. +@param aCharWidth Width of the image. +@param aRepeatCount Number of lines to copy. +*/ +static void CopyCharLine(TUint32*& aBinaryDataPtr,TInt aBufferWords,const TUint8* aData,TInt aBitShift,TInt aCharWidth, TInt16 aRepeatCount) + { + aBitShift&=7; + TInt wordstocopy=(aCharWidth+31)>>5; + if(wordstocopy>aBufferWords) wordstocopy=aBufferWords; + TUint32* ptrlimit=aBinaryDataPtr+wordstocopy; + TUint32* dataword=(TUint32*)(TInt(aData)&~3); + aBitShift+=(TInt(aData)-TInt(dataword))<<3; + + TUint32* startBinaryDataPtr = aBinaryDataPtr; + while(aBinaryDataPtr>=aBitShift; + if(aBitShift) *aBinaryDataPtr|=(*dataword<<(32-aBitShift)); + aBinaryDataPtr++; + } + + TUint32* curStartBinaryDataPtr = aBinaryDataPtr; + TInt byteToCopy = wordstocopy << 2; + while(aRepeatCount > 1) + { + Mem::Copy(curStartBinaryDataPtr, startBinaryDataPtr, byteToCopy); + curStartBinaryDataPtr += wordstocopy; + + aRepeatCount--; + } + aBinaryDataPtr = curStartBinaryDataPtr; + } + +/** +Static utility function. Decodes a monochrome glyph whose data is run length encoded, +into a 1bpp bitmap. +@param aDataSize Image size in pixels. +@param aData Pointer to a source buffer. +@param aStride Image data stride. +@param aBinaryData Pointer to a destination buffer. This buffer must be allocated + by the caller. +*/ +static void DecodeBinaryData(const TSize& aDataSize, const TUint8* aData, TInt aStride, + TUint32* aBinaryData) + { + const TInt datalength = aDataSize.iWidth; + const TInt dataheight = aDataSize.iHeight; + TInt bitindex=0; + TInt16 repeatcount=0; + TUint32* slbuffer=aBinaryData; + const TInt slwords=aStride; + + for(TInt charline=0;charline>3)); + repeatcount>>=bitindex&7; + const TInt multilineflag=repeatcount&1; + repeatcount>>=1; + repeatcount&=0xf; + bitindex+=5; + if(multilineflag) + { + for(TInt currentline=0;currentline>3),bitindex&7,datalength, 1); + bitindex+=datalength; + } + } + else + { + CopyCharLine(slbuffer,slwords,aData+(bitindex>>3),bitindex&7,datalength, repeatcount); + bitindex+=datalength; + } + } + } +//-------------- +__CONSTRUCT_STEP__(FbsGlyphData)