#include #include #include #include #include constexpr static int k_tabCharacterWidth = 4; KDPoint KDContext::drawString(const char * text, KDPoint p, const KDFont * font, KDColor textColor, KDColor backgroundColor, int maxByteLength) { return pushOrPullString(text, p, font, textColor, backgroundColor, maxByteLength, true); } int KDContext::checkDrawnString(const char * text, KDPoint p, const KDFont * font, KDColor textColor, KDColor backgroundColor, int maxLength) { int numberOfFailedPixels = 0; pushOrPullString(text, p, font, textColor, backgroundColor, maxLength, false, &numberOfFailedPixels); return numberOfFailedPixels; } KDPoint KDContext::pushOrPullString(const char * text, KDPoint p, const KDFont * font, KDColor textColor, KDColor backgroundColor, int maxByteLength, bool push, int * result) { KDPoint position = p; KDSize glyphSize = font->glyphSize(); KDFont::RenderPalette palette = font->renderPalette(textColor, backgroundColor); KDFont::GlyphBuffer glyphBuffer; UTF8Decoder decoder(text); const char * codePointPointer = decoder.stringPosition(); CodePoint codePoint = decoder.nextCodePoint(); while (codePoint != UCodePointNull && (maxByteLength < 0 || codePointPointer < text + maxByteLength)) { codePointPointer = decoder.stringPosition(); if (codePoint == UCodePointLineFeed) { position = KDPoint(0, position.y() + glyphSize.height()); codePoint = decoder.nextCodePoint(); } else if (codePoint == UCodePointTabulation) { position = position.translatedBy(KDPoint(k_tabCharacterWidth * glyphSize.width(), 0)); codePoint = decoder.nextCodePoint(); } else { assert(!codePoint.isCombining()); font->setGlyphGreyscalesForCodePoint(codePoint, &glyphBuffer); codePoint = decoder.nextCodePoint(); while (codePoint.isCombining()) { font->accumulateGlyphGreyscalesForCodePoint(codePoint, &glyphBuffer); codePointPointer = decoder.stringPosition(); codePoint = decoder.nextCodePoint(); } font->colorizeGlyphBuffer(&palette, &glyphBuffer); if (push) { // Push the character on the screen fillRectWithPixels( KDRect(position, glyphSize), glyphBuffer.colorBuffer(), glyphBuffer.colorBuffer() // It's OK to trash the content of the color buffer since we'll re-fetch it for the next char anyway ); } else { // Pull and compare the character from the screen assert(result != nullptr); *result = 0; KDFont::GlyphBuffer workingGlyphBuffer; KDColor * workingColorBuffer = workingGlyphBuffer.colorBuffer(); KDColor * colorBuffer = glyphBuffer.colorBuffer(); for (int i = 0; i < glyphSize.height() * glyphSize.width(); i++) { workingColorBuffer[i] = KDColorRed; } /* Caution: Unlike fillRectWithPixels, pullRect accesses outside (0, 0, * Ion::Display::Width, Ion::Display::Height) might give weird data. */ Ion::Display::pullRect(KDRect(position, glyphSize), workingColorBuffer); for (int k = 0; k < glyphSize.height() * glyphSize.width(); k++) { if (colorBuffer[k] != workingColorBuffer[k]) { *result = (*result)+1; break; } } } position = position.translatedBy(KDPoint(glyphSize.width(), 0)); } } return position; }