diff --git a/tests/comparerenderer/CompareRenderer.cpp b/tests/comparerenderer/CompareRenderer.cpp index 8f7574d..6c98f9a 100644 --- a/tests/comparerenderer/CompareRenderer.cpp +++ b/tests/comparerenderer/CompareRenderer.cpp @@ -372,6 +372,7 @@ int main(int argc, char ** argv) { fprintf(stderr, "Please specify at least 1 renderer\n"); showOptions(); + if (rendererOptions[OptLogFile].exists()) fclose(log); return -3; } diff --git a/tests/comparerenderer/CompareRenderer.cpp.leaked_storage b/tests/comparerenderer/CompareRenderer.cpp.leaked_storage new file mode 100644 index 0000000..8f7574d --- /dev/null +++ b/tests/comparerenderer/CompareRenderer.cpp.leaked_storage @@ -0,0 +1,407 @@ +/* GRAPHITE2 LICENSING + + Copyright 2010, SIL International + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should also have received a copy of the GNU Lesser General Public + License along with this library in the file named "LICENSE". + If not, write to the Free Software Foundation, 51 Franklin Street, + Suite 500, Boston, MA 02110-1335, USA or visit their web page on the + internet at http://www.fsf.org/licenses/lgpl.html. +*/ +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef __GNUC__ +#include +#ifndef __linux__ +#include +#endif +#endif + +#ifdef WIN32 +#include +#endif + +#include "RendererOptions.h" +#include "Renderer.h" +#include "RenderedLine.h" +#include "FeatureParser.h" + +#include "Gr2Renderer.h" +#include "graphite2/Log.h" + +const size_t NUM_RENDERERS = 6; + +class CompareRenderer +{ +public: + CompareRenderer(const char * testFile, Renderer** renderers, bool verbose) + : m_fileBuffer(NULL), m_numLines(0), m_lineOffsets(NULL), + m_renderers(renderers), m_verbose(verbose), m_cfMask(ALL_DIFFERENCE_TYPES) + { + // read the file into memory for fast access + struct stat fileStat; + if (stat(testFile, &fileStat) == 0) + { + FILE * file = fopen(testFile, "rb"); + if (file) + { + m_fileBuffer = new char[fileStat.st_size]; + if (m_fileBuffer) + { + m_fileLength = fread(m_fileBuffer, 1, fileStat.st_size, file); + assert(m_fileLength == fileStat.st_size); + countLines(); + findLines(); + for (size_t r = 0; r < NUM_RENDERERS; r++) + { + if (m_renderers[r]) + { + m_lineResults[r] = new RenderedLine[m_numLines]; + } + else + { + m_lineResults[r] = NULL; + } + m_elapsedTime[r] = 0.0f; + m_glyphCount[r] = 0; + } + } + fclose(file); + } + else + { + fprintf(stderr, "Error opening file %s\n", testFile); + } + } + else + { + fprintf(stderr, "Error stating file %s\n", testFile); + for (size_t r = 0; r < NUM_RENDERERS; r++) + { + m_lineResults[r] = NULL; + m_elapsedTime[r] = 0.0f; + } + } + } + + ~CompareRenderer() + { + delete [] m_fileBuffer; + m_fileBuffer = NULL; + for (size_t i = 0; i < NUM_RENDERERS; i++) + { + if (m_lineResults[i]) delete [] m_lineResults[i]; + m_lineResults[i] = NULL; + } + if (m_lineOffsets) delete [] m_lineOffsets; + m_lineOffsets = NULL; + } + + void runTests(FILE * log, int repeat = 1) + { + for (size_t r = 0; r < NUM_RENDERERS; r++) + { + if (m_renderers[r]) + { + for (int i = 0; i < repeat; i++) + m_elapsedTime[r] += runRenderer(*m_renderers[r], m_lineResults[r], m_glyphCount[r], log); + fprintf(stdout, "Ran %s in %fs (%lu glyphs)\n", m_renderers[r]->name(), m_elapsedTime[r], m_glyphCount[r]); + } + } + } + int compare(float tolerance, float fractionalTolerance, FILE * log) + { + int status = IDENTICAL; + for (size_t i = 0; i < NUM_RENDERERS; i++) + { + for (size_t j = i + 1; j < NUM_RENDERERS; j++) + { + if (m_renderers[i] == NULL || m_renderers[j] == NULL) continue; + if (m_lineResults[i] == NULL || m_lineResults[j] == NULL) continue; + fprintf(log, "Comparing %s with %s\n", m_renderers[i]->name(), m_renderers[j]->name()); + for (size_t line = 0; line < m_numLines; line++) + { + LineDifference ld = m_lineResults[i][line].compare(m_lineResults[j][line], tolerance, fractionalTolerance); + ld = (LineDifference)(m_cfMask & ld); + if (ld) + { + fprintf(log, "Line %u %s\n", (unsigned int)line, DIFFERENCE_DESC[ld]); + for (size_t c = m_lineOffsets[line]; c < m_lineOffsets[line+1]; c++) + { + fprintf(log, "%c", m_fileBuffer[c]); + } + fprintf(log, "\n"); + m_lineResults[i][line].dump(log); + fprintf(log, "%s\n", m_renderers[i]->name()); + m_lineResults[j][line].dump(log); + fprintf(log, "%s\n", m_renderers[j]->name()); + status |= ld; + } + } + } + } + return status; + } + void setDifferenceMask(LineDifference m) { m_cfMask = m; } +protected: + float runRenderer(Renderer & renderer, RenderedLine * pLineResult, unsigned long & glyphCount, FILE *log) + { + glyphCount = 0; + unsigned int i = 0; + const char * pLine = m_fileBuffer; +#ifdef __linux__ + struct timespec startTime; + struct timespec endTime; + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &startTime); +#else +#ifdef WIN32 + LARGE_INTEGER counterFreq; + LARGE_INTEGER startCounter; + LARGE_INTEGER endCounter; + if (!QueryPerformanceFrequency(&counterFreq)) + fprintf(stderr, "Warning no high performance counter available\n"); + QueryPerformanceCounter(&startCounter); +#else + struct timeval startTime; + struct timeval endTime; + gettimeofday(&startTime,0); +#endif +#endif + // check for CRLF + int lfLength = 1; + if ((m_numLines > 1) && (m_lineOffsets[1] > 2) && (m_fileBuffer[m_lineOffsets[1]-2] == '\r')) + lfLength = 2; + if (m_verbose) + { + fprintf(log, "[\n"); + while (i < m_numLines) + { + size_t lineLength = m_lineOffsets[i+1] - m_lineOffsets[i] - lfLength; + pLine = m_fileBuffer + m_lineOffsets[i]; + renderer.renderText(pLine, lineLength, pLineResult + i, log); + pLineResult[i].dump(log); + glyphCount += pLineResult[i].numGlyphs(); + ++i; + } + fprintf(log, "]\n"); + } + else + { + while (i < m_numLines) + { + size_t lineLength = m_lineOffsets[i+1] - m_lineOffsets[i] - lfLength; + pLine = m_fileBuffer + m_lineOffsets[i]; + renderer.renderText(pLine, lineLength, pLineResult + i, log); + glyphCount += pLineResult[i].numGlyphs(); + ++i; + } + } + float elapsed = 0.; +#ifdef __linux__ + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &endTime); + long deltaSeconds = endTime.tv_sec - startTime.tv_sec; + long deltaNs = endTime.tv_nsec - startTime.tv_nsec; + if (deltaNs < 0) + { + deltaSeconds -= 1; + deltaNs += 1000000000; + } + elapsed = deltaSeconds + deltaNs / 1000000000.0f; +#else +#ifdef WIN32 + QueryPerformanceCounter(&endCounter); + elapsed = (endCounter.QuadPart - startCounter.QuadPart) / static_cast(counterFreq.QuadPart); +#else + gettimeofday(&endTime,0); + long deltaSeconds = endTime.tv_sec - startTime.tv_sec; + long deltaUs = endTime.tv_usec - startTime.tv_usec; + if (deltaUs < 0) + { + deltaSeconds -= 1; + deltaUs += 1000000; + } + elapsed = deltaSeconds + deltaUs / 1000000.0f; +#endif +#endif + return elapsed; + } + + size_t countLines() + { + for (size_t i = 0; i < m_fileLength; i++) + { + if (m_fileBuffer[i] == '\n') + { + ++m_numLines; + } + } + return m_numLines; + } + void findLines() + { + m_lineOffsets = new size_t[m_numLines+1]; + m_lineOffsets[0] = 0; + int line = 0; + for (size_t i = 0; i < m_fileLength; i++) + { + if (m_fileBuffer[i] == '\n') + { + m_lineOffsets[++line] = i + 1; + } + if (m_fileBuffer[i] > 0 && m_fileBuffer[i] < 32) + m_fileBuffer[i] = 32; + } + m_lineOffsets[m_numLines] = m_fileLength; + } +private: + char * m_fileBuffer; + size_t m_fileLength; + size_t m_numLines; + size_t * m_lineOffsets; + Renderer** m_renderers; + RenderedLine * m_lineResults[NUM_RENDERERS]; + float m_elapsedTime[NUM_RENDERERS]; + unsigned long m_glyphCount[NUM_RENDERERS]; + bool m_verbose; + LineDifference m_cfMask; +}; + + +int main(int argc, char ** argv) +{ + if (!parseOptions(argc, argv) || + !rendererOptions[OptFontFile].exists() || + !rendererOptions[OptTextFile].exists() || + !rendererOptions[OptSize].exists()) + { + fprintf(stderr, "Usage:\n%s [options] -t utf8.txt -f font.ttf -s 12\n", argv[0]); + fprintf(stderr, "Options:\n"); + showOptions(); + return -1; + } + + const char * textFile = rendererOptions[OptTextFile].get(argv); + const char * fontFile = rendererOptions[OptFontFile].get(argv); + int fontSize = rendererOptions[OptSize].getInt(argv); + FILE * log = stdout; + if (rendererOptions[OptLogFile].exists()) + { + log = fopen(rendererOptions[OptLogFile].get(argv), "wb"); + if (!log) + { + fprintf(stderr, "Failed to open log file %s\n", + rendererOptions[OptLogFile].get(argv)); + return -2; + } + } + + + Renderer* renderers[NUM_RENDERERS] = {NULL, NULL, NULL, NULL, NULL}; + FeatureParser * featureSettings = NULL; + FeatureParser * altFeatureSettings = NULL; + int direction = (rendererOptions[OptRtl].exists())? 1 : 0; + int segCacheSize = rendererOptions[OptSegCache].getInt(argv); + const std::string traceLogPath = rendererOptions[OptTrace].exists() ? rendererOptions[OptTrace].get(argv) : std::string(); + Gr2Face face(fontFile, traceLogPath, rendererOptions[OptDemand].get(argv)); + + + if (rendererOptions[OptFeatures].exists()) + { + featureSettings = new FeatureParser(rendererOptions[OptFeatures].get(argv)); + } + + if (rendererOptions[OptQuiet].exists()) + { + fclose(stderr); + } + + if (rendererOptions[OptAlternativeFont].exists()) + { + if (rendererOptions[OptAltFeatures].exists()) + { + altFeatureSettings = new FeatureParser(rendererOptions[OptAltFeatures].get(argv)); + } + else + { + altFeatureSettings = featureSettings; + } + const char * altFontFile = rendererOptions[OptAlternativeFont].get(argv); + if (rendererOptions[OptGraphite2].exists()) + { + std::string altTraceLogPath = traceLogPath; + altTraceLogPath.insert(traceLogPath.find_last_of('.'), ".alt"); + Gr2Face altFace(altFontFile, altTraceLogPath, rendererOptions[OptDemand].get(argv)); + + renderers[0] = new Gr2Renderer(face, fontSize, direction, featureSettings); + renderers[1] = new Gr2Renderer(altFace, fontSize, direction, altFeatureSettings); + } + } + else + { + if (rendererOptions[OptGraphite2].exists()) + renderers[0] = new Gr2Renderer(face, fontSize, direction, featureSettings); + + if (rendererOptions[OptGraphite2s].exists()) + { + Gr2Face uncached(fontFile, + std::string(traceLogPath).insert(traceLogPath.find_last_of('.'), ".uncached"), rendererOptions[OptDemand].get(argv)); + renderers[1] = new Gr2Renderer(uncached, fontSize, direction, featureSettings); + } + } + + if (renderers[0] == NULL && renderers[1] == NULL) + { + fprintf(stderr, "Please specify at least 1 renderer\n"); + showOptions(); + return -3; + } + + CompareRenderer compareRenderers(textFile, renderers, rendererOptions[OptVerbose].exists()); + if (rendererOptions[OptRepeat].exists()) + compareRenderers.runTests(log, rendererOptions[OptRepeat].getInt(argv)); + else + compareRenderers.runTests(log); + // set compare options + if (rendererOptions[OptIgnoreGlyphIdDifferences].exists()) + { + compareRenderers.setDifferenceMask((LineDifference)(ALL_DIFFERENCE_TYPES ^ DIFFERENT_GLYPHS)); + } + int status = 0; + if (rendererOptions[OptCompare].exists()) + status = compareRenderers.compare(rendererOptions[OptTolerance].getFloat(argv), + rendererOptions[OptFractionalTolerance].getFloat(argv), log); + + for (size_t i = 0; i < NUM_RENDERERS; i++) + { + if (renderers[i]) + { + delete renderers[i]; + renderers[i] = NULL; + } + } + if (altFeatureSettings != featureSettings) + delete altFeatureSettings; + delete featureSettings; + if (rendererOptions[OptLogFile].exists()) fclose(log); + + return status; +}