--- /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 <EGL/egl.h>
+#include <VG/openvg.h>
+#include <graphics/fbsglyphmetricsarray.h>
+#include <graphics/fbsglyphdataiterator.h>
+#include <sgresource/sgimage.h>
+#include <sgresource/sgdriver_test.h>
+#include <sgresource/sgdriver_profiling.h>
+#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<RSgImage>& aImages);
+//static TInt NearlyFillGraphicsMemoryWithImages(const TSize& aSize, RArray<RSgImage>& 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<TUint8*>(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<TUint8*>(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<KMaxFileName> 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<KMaxFileName> 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<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
+ eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
+ vgCreateImageTargetKHR = reinterpret_cast<TvgCreateEGLImageTargetKHRTypefPtr>(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<EGLClientBuffer>(&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 <RSgImage> 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 <RSgImage> 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 <RSgImage> 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<RSemaphore*>(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<KMaxFileName> buf;
+ buf.AppendNum( countToAppend );
+ TPtrC nameAppend( buf );
+
+ SaveBmp(bitmap, &nameAppend, EFalse);
+ }
+ delete bitmap;
+ if (KErrNone == CreateBitmapFromSgImage(aEGL, aImageB, aRectB, bitmap))
+ {
+ TBuf<KMaxFileName> 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<TGlyphDataThreadInfo*>(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<RSgImage>& 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<RSgImage>& 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<TUint8*>(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<TUint32*&>(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<ptrlimit)
+ {
+ *aBinaryDataPtr=*dataword++;
+ *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<dataheight;charline+=repeatcount) // for lines in the character...
+ {
+ repeatcount=CFbsBitGc::Load16(aData+(bitindex>>3));
+ repeatcount>>=bitindex&7;
+ const TInt multilineflag=repeatcount&1;
+ repeatcount>>=1;
+ repeatcount&=0xf;
+ bitindex+=5;
+ if(multilineflag)
+ {
+ for(TInt currentline=0;currentline<repeatcount;currentline++)
+ {
+ CopyCharLine(slbuffer,slwords,aData+(bitindex>>3),bitindex&7,datalength, 1);
+ bitindex+=datalength;
+ }
+ }
+ else
+ {
+ CopyCharLine(slbuffer,slwords,aData+(bitindex>>3),bitindex&7,datalength, repeatcount);
+ bitindex+=datalength;
+ }
+ }
+ }
+//--------------
+__CONSTRUCT_STEP__(FbsGlyphData)