#pragma once #include #include #include #include #include #include #include #include #include #include Framework::Schrift* schrift; class CharMask { private: bool* mask; int width; int height; Framework::Text result; public: CharMask(Framework::Datei* d) { int l = 0; d->lese((char*)&l, 4); char* txt = new char[l + 1]; d->lese(txt, l); txt[l] = 0; result.setText(txt, l); delete[] txt; d->lese((char*)&width, 4); d->lese((char*)&height, 4); mask = new bool[width * height]; d->lese((char*)mask, width * height); } CharMask(int width, int height) { mask = new bool[width * height]; this->width = width; this->height = height; for (int i = 0; i < width * height; i++) { mask[i] = 0; } result = ""; } ~CharMask() { delete[] mask; } void save(Framework::Datei* d) { int l = result.getLength(); d->schreibe((char*)&l, 4); d->schreibe(result.getText(), l); d->schreibe((char*)&width, 4); d->schreibe((char*)&height, 4); d->schreibe((char*)mask, width * height); } bool isEmpty() { return width == 0 && height == 0; } void adjustSize() { int maxX = 0; int maxY = 0; int minX = width; int minY = height; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (mask[y * width + x]) { if (x > maxX) maxX = x; if (y > maxY) maxY = y; if (x < minX) minX = x; if (y < minY) minY = y; } } } if (maxX < minX || maxY < minY) { width = 0; height = 0; delete[] mask; mask = 0; return; } int newWidth = maxX - minX + 1; int newHeight = maxY - minY + 1; bool* newMask = new bool[newWidth * newHeight]; for (int x = 0; x < newWidth; x++) { for (int y = 0; y < newHeight; y++) { newMask[y * newWidth + x] = mask[(minY + y) * width + (minX + x)]; } } delete[] mask; mask = newMask; width = newWidth; height = newHeight; } bool toggleMask(int x, int y) { if (x < 0 || x >= width || y < 0 || y >= height) { return 0; } return mask[y * width + x] = !mask[y * width + x]; } int getWidth() { return width; } int getHeight() { return height; } Framework::Text& getResult() { return result; } bool* getMask() { return mask; } int pixelCount() { int result = 0; for (int i = 0; i < width * height; i++) { if (mask[i]) { result++; } } return result; } }; Framework::Array* chars; void saveChars() { Framework::Datei d("chars.dat"); d.remove(); d.erstellen(); d.open(Framework::Datei::Style::schreiben); for (CharMask* charMask : *chars) { charMask->save(&d); } d.close(); } void addChar(CharMask* charMask) { for (int i = 0; i < chars->getEintragAnzahl(); i++) { if (charMask->getWidth() * charMask->getHeight() > chars->get(i)->getWidth() * chars->get(i)->getHeight()) { chars->add(charMask, i); return; } if (charMask->getWidth() * charMask->getHeight() == chars->get(i)->getWidth() * chars->get(i)->getHeight() && charMask->pixelCount() > chars->get(i)->pixelCount()) { chars->add(charMask, i); return; } } chars->add(charMask); } void loadChars() { Framework::Trie counts; chars = new Framework::Array(); Framework::Datei d("chars.dat"); if (d.existiert()) { d.open(Framework::Datei::Style::lesen); while (!d.istEnde()) { CharMask* charMask = new CharMask(&d); counts.set(charMask->getResult().getText(), charMask->getResult().getLength(), counts.get(charMask->getResult().getText(), charMask->getResult().getLength()) + 1); if (charMask->isEmpty() || !charMask->getResult().getLength()) { delete charMask; continue; } // addChar(charMask); chars->add(charMask); } d.close(); } Framework::Trie output; for (CharMask* mask : *chars) { if (!output.get( mask->getResult().getText(), mask->getResult().getLength())) { output.set(mask->getResult().getText(), mask->getResult().getLength(), true); std::cout << "Loaded " << counts.get(mask->getResult().getText(), mask->getResult().getLength()) << " variants of '" << mask->getResult().getText() << "'" << std::endl; } } std::cout << "loaded " << chars->getEintragAnzahl() << " character variants" << std::endl; } struct Step { int x; int y; int width; int height; bool* map; Framework::Text result; }; class TextAnalyser { private: int width; int height; bool* mask; int backgroundColor; Framework::Text result; bool image; Framework::WFenster* zf; Framework::BildZ* z; public: TextAnalyser(Framework::WFenster* zf) : width(0), height(0), mask(0), backgroundColor(0), image(0), zf(zf), z(0) {} ~TextAnalyser() { if (mask) { delete[] mask; } if (z) { zf->zBildschirm()->removeMember(z); } } RECT findRect(int width, int height, char* bitmapData) { RECT result = {0, 0, width, height}; int x = 25; int y = 25; int stride = (width * 4); backgroundColor = (bitmapData[y * stride + x * 4] << 16 & 0x00FF0000) | (bitmapData[y * stride + x * 4 + 1] << 8 & 0x0000FF00) | (bitmapData[y * stride + x * 4 + 2] & 0x000000FF); bool inside = 1; for (int i = x; i > 0; i--) { int color = (bitmapData[y * stride + i * 4] << 16 & 0x00FF0000) | (bitmapData[y * stride + i * 4 + 1] << 8 & 0x0000FF00) | (bitmapData[y * stride + i * 4 + 2] & 0x000000FF); if (color != backgroundColor) { if (inside) { result.left = i + 1; } inside = 0; } else { inside = 1; } } inside = 1; for (int i = x; i < width; i++) { int color = (bitmapData[y * stride + i * 4] << 16 & 0x00FF0000) | (bitmapData[y * stride + i * 4 + 1] << 8 & 0x0000FF00) | (bitmapData[y * stride + i * 4 + 2] & 0x000000FF); if (color != backgroundColor) { if (inside) { result.right = i - 1; } inside = 0; } else { inside = 1; } } inside = 1; for (int i = y; i > 0; i--) { int color = (bitmapData[i * stride + x * 4] << 16 & 0x00FF0000) | (bitmapData[i * stride + x * 4 + 1] << 8 & 0x0000FF00) | (bitmapData[i * stride + x * 4 + 2] & 0x000000FF); if (color != backgroundColor) { if (inside) { result.top = i + 1; } inside = 0; } else { inside = 1; } } inside = 1; for (int i = y; i < height; i++) { int color = (bitmapData[i * stride + x * 4] << 16 & 0x00FF0000) | (bitmapData[i * stride + x * 4 + 1] << 8 & 0x0000FF00) | (bitmapData[i * stride + x * 4 + 2] & 0x000000FF); if (color != backgroundColor) { if (inside) { result.bottom = i - 1; } inside = 0; } else { inside = 1; } } result.top += 85; this->width = result.right - result.left; this->height = result.bottom - result.top; mask = new bool[width * height]; for (int x = 0; x < this->width; x++) { for (int y = 0; y < this->height; y++) { int color = (bitmapData[(result.bottom - 1 - y) * stride + (result.left + x) * 4] << 16 & 0x00FF0000) | (bitmapData[(result.bottom - 1 - y) * stride + (result.left + x) * 4 + 1] << 8 & 0x0000FF00) | (bitmapData[(result.bottom - 1 - y) * stride + (result.left + x) * 4 + 2] & 0x000000FF); unsigned int r = bitmapData[(result.bottom - 1 - y) * stride + (result.left + x) * 4] & 0xFF; unsigned int g = bitmapData[(result.bottom - 1 - y) * stride + (result.left + x) * 4 + 1] & 0xFF; unsigned int b = bitmapData[(result.bottom - 1 - y) * stride + (result.left + x) * 4 + 2] & 0xFF; unsigned int bgr = ((backgroundColor & 0xFF0000) >> 16) & 0xFF; unsigned int bgg = ((backgroundColor & 0xFF00) >> 8) & 0xFF; unsigned int bgb = backgroundColor & 0xFF; int diff = std::abs((int)(bgr - r)) + std::abs((int)(bgg - g)) + std::abs((int)(bgb - b)); mask[y * this->width + x] = diff > 200; } } if (isEmpty(30, 0, this->width - 60, 20)) { analyseParagraph(30, 55, this->width - 60, this->height - 55); } else { analyseParagraph(30, 0, this->width - 60, this->height); } return result; } bool isEmpty(int x, int y, int width, int height) { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (mask[(y + j) * this->width + (x + i)]) { return false; } } } return true; } void analyseParagraph(int x, int y, int width, int height) { while (isEmpty(x, y, width, 1) && height > 0) { y++; height--; } while (isEmpty(x, y + height - 1, width, 1) && height > 0) { height--; } while (isEmpty(x, y, 1, height) && width > 0) { x++; width--; } while (isEmpty(x + width - 1, y, 1, height) && width > 0) { width--; } int mid = x + width / 2; if (isEmpty(mid - 3, y, 6, height)) { analyseParagraph(x, y, mid - 3 - x, height); analyseParagraph(mid + 3, y, width / 2 - 3, height); } else { bool found = 0; for (int i = 1; i < height; i++) { if (isEmpty(x, y + i, width, 3)) { analyseLine(x, y, width, i); y += i + 3; height -= i + 3; while (isEmpty(x, y, width, 1)) { y++; height--; } i = 0; found = 1; } } if (found) { if (!isEmpty(x, y, width, height)) { analyseLine(x, y, width, height); } } else { image = 1; } } } void analyseWord(int x, int y, int width, int height) { Framework::Array steps; while (true) { bool found = 0; for (CharMask* charMask : *chars) { if (charMask->getWidth() <= width && charMask->getHeight() <= height) { int yOffset = 0; while (yOffset + charMask->getHeight() <= height) { bool match = 1; for (int i = 0; i < charMask->getWidth(); i++) { for (int j = 0; j < charMask->getHeight(); j++) { if (charMask->getMask()[j * charMask->getWidth() + i] && !mask[(j + y + yOffset) * this->width + x + i]) { match = 0; break; } } if (!match) { break; } } if (match && (charMask->getResult().istGleich(",") || charMask->getResult().istGleich("."))) { if (yOffset < (height - charMask->getHeight()) / 2) { match = 0; } } if (match) { if (charMask->getResult().istGleich("I") && steps.getEintragAnzahl() > 1 && steps.get(steps.getEintragAnzahl() - 1) .result.istGleich("-") && steps.get(steps.getEintragAnzahl() - 2) .result.istGleich("I")) { std::cout << "warning: I-I detected" << std::endl; } if (steps.getEintragAnzahl() > 0 && (steps.get(steps.getEintragAnzahl() - 1) .result.istGleich(",") || steps.get(steps.getEintragAnzahl() - 1) .result.istGleich("."))) { std::cout << "warning: . or , inside of a word " "detected" << std::endl; } Step step = { x, y, width, height, new bool[width * height]}; step.result = charMask->getResult().getText(); memset(step.map, 0, width * height); found = 1; for (int i = 0; i < charMask->getWidth(); i++) { for (int j = 0; j < charMask->getHeight(); j++) { if (charMask ->getMask()[j * charMask->getWidth() + i]) { mask[(j + y + yOffset) * this->width + x + i] = 0; step.map[(j + yOffset) * width + i] = 1; } else { step.map[(j + yOffset) * width + i] = 0; } } } steps.add(step); break; } yOffset++; } } } if (!found) { Framework::Text word; for (const Step& step : steps) { word.append(step.result.getText()); } std::cout << "What comes after: " << word.getText() << std::endl; while (!render(x, y, width, height)) { if (steps.getEintragAnzahl() > 0) { Step last = steps.get(steps.getEintragAnzahl() - 1); steps.remove(steps.getEintragAnzahl() - 1); x = last.x; y = last.y; width = last.width; height = last.height; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (last.map[j * width + i]) { mask[(j + y) * this->width + x + i] = 1; } } } delete[] last.map; } } } else { while (isEmpty(x, y, 1, height) && width > 0) { x++; width--; } while (isEmpty(x, y, width, 1) && height > 0) { y++; height--; } while (isEmpty(x + width - 1, y, 1, height) && width > 0) { width--; } while (isEmpty(x, y + height - 1, width, 1) && height > 0) { height--; } } if (width <= 0 || height <= 0) { break; } } Framework::Text word; for (const Step& step : steps) { word.append(step.result.getText()); delete[] step.map; } std::cout << "Read word: " << word.getText() << std::endl; this->result.append() << word.getText() << " "; } void analyseLine(int x, int y, int width, int height) { int blankWidth = 3; while (isEmpty(x, y, 1, height) && width > 0) { x++; width--; } while (isEmpty(x + width - 1, y, 1, height) && width > 0) { width--; } for (int i = 1; i < width; i++) { if (isEmpty(x + i, y, blankWidth, height)) { analyseWord(x, y, i, height); x += i + blankWidth; width -= i + blankWidth; while (isEmpty(x, y, 1, height)) { x++; width--; } i = 0; } } if (width > 0 && !isEmpty(x, y, width, height)) { analyseWord(x, y, width, height); } if (result.getLength() > 1 && result.getText()[result.getLength() - 2] == '.') // last char is allways space { result.append() << "
"; } result.append() << "\n"; } bool render(int x, int y, int width, int height) { CharMask* charMask = new CharMask(width, height); int factor = 5; zf->setSize(width * factor + 10, height * factor + 35); Framework::Bildschirm* screen = zf->zBildschirm(); zf->setAnzeigeModus(1); screen->setBackBufferSize(width * factor + 10, height * factor + 35); screen->update(); if (z) { screen->removeMember(z); } z = new Framework::BildZ(); Framework::Bild* b = new Framework::Bild(); b->neuBild(width * factor, height * factor, 0xFF000000); for (int i = 0; i < width * factor; i++) { for (int j = 0; j < height * factor; j++) { if (mask[(j / factor + y) * this->width + i / factor + x]) { b->setPixelDP(i, j, 0xFFFFFFFF); } } } z->setBildZ(b); z->setSize(width * factor, height * factor); z->setPosition(5, 5); z->setStyle(Framework::BildZ::Style::Sichtbar | Framework::BildZ::Style::Erlaubt); Framework::UIInit init = Framework::defaultUI(schrift, screen); Framework::TextFeld* tf = init.createTextFeld(init.initParam); tf->setSize(width * factor, 20); tf->setStyle(Framework::TextFeld::Style::TextFeld); tf->setPosition(5, height * factor + 10); tf->setText(""); tf->setMausEreignis(Framework::_ret1ME); tf->setTastaturEreignis(Framework::_ret1TE); screen->addMember(tf); screen->addMember(z); screen->render(); bool wait = 1; int lastX = 0; int lastY = 0; bool currentState = 0; bool aborted = 0; int size = 3; z->setMausEreignis([&wait, b, &charMask, width, height, factor, screen, this, x, y, &lastX, &lastY, ¤tState, &aborted, &size, tf]( void* z, void* p, Framework::MausEreignis me) { if (me.id == Framework::ME_PRechts) { charMask->adjustSize(); if (charMask->isEmpty()) { delete charMask; charMask = new CharMask(width, height); } else { if (tf->getText()->getLength() > 0) { wait = 0; } } } if (me.id == Framework::ME_PMitte) { aborted = 1; wait = 0; } if (me.id == Framework::ME_PLinks) { if (mask[(me.my / factor + y) * this->width + me.mx / factor + x]) { currentState = charMask->toggleMask(me.mx / factor, me.my / factor); for (int x2 = -size / 2; x2 < (size - size / 2); x2++) { for (int y2 = -size / 2; y2 < (size - size / 2); y2++) { if (me.my / factor + y2 >= 0 && me.my / factor + y2 < charMask->getHeight() && me.my / factor + y2 + y < this->height && me.mx / factor + x2 >= 0 && me.mx / factor + x2 < charMask->getWidth() && me.mx / factor + x2 + x < this->width) { if (mask[(me.my / factor + y2 + y) * this->width + me.mx / factor + x2 + x]) { charMask ->getMask()[(me.my / factor + y2) * charMask->getWidth() + me.mx / factor + x2] = currentState; for (int xx = 0; xx < factor; xx++) { for (int yy = 0; yy < factor; yy++) { if (currentState) { b->setPixelDP( (me.mx / factor + x2) * factor + xx, (me.my / factor + y2) * factor + yy, 0xFF00FF00); } else { b->setPixelDP( (me.mx / factor + x2) * factor + xx, (me.my / factor + y2) * factor + yy, 0xFFFFFFFF); } } } } } } } } screen->render(); } if (me.id == Framework::ME_Bewegung) { int px = me.mx / factor; int py = me.my / factor; if (px != lastX || py != lastY) { for (int x2 = -size / 2; x2 < (size - size / 2); x2++) { for (int y2 = -size / 2; y2 < (size - size / 2); y2++) { if (lastY + y2 >= 0 && lastY + y2 < charMask->getHeight() && lastY + y2 + y < this->height && lastX + x2 >= 0 && lastX + x2 < charMask->getWidth() && lastX + x2 + x < this->width) { if (charMask ->getMask()[(lastY + y2) * charMask->getWidth() + lastX + x2]) { for (int x = 0; x < factor; x++) { for (int y = 0; y < factor; y++) { b->setPixelDP( (lastX + x2) * factor + x, (lastY + y2) * factor + y, 0xFF00FF00); } } } else { if (mask[(lastY + y + y2) * this->width + lastX + x + x2]) { for (int x = 0; x < factor; x++) { for (int y = 0; y < factor; y++) { b->setPixelDP( (lastX + x2) * factor + x, (lastY + y2) * factor + y, 0xFFFFFFFF); } } } } } } } for (int x2 = -size / 2; x2 < (size - size / 2); x2++) { for (int y2 = -size / 2; y2 < (size - size / 2); y2++) { if (py + y2 >= 0 && py + y2 < charMask->getHeight() && py + y2 + y < this->height && px + x2 >= 0 && px + x2 < charMask->getWidth() && px + x2 + x < this->width) { if (mask[(py + y + y2) * this->width + px + x + x2]) { for (int x = 0; x < factor; x++) { for (int y = 0; y < factor; y++) { b->setPixelDP( (px + x2) * factor + x, (py + y2) * factor + y, 0xFFFFA000); } } if (Framework::getMausStand( Framework::M_Links)) { charMask->getMask() [(py + y2) * charMask->getWidth() + px + x2] = currentState; if (currentState) { for (int x = 0; x < factor; x++) { for (int y = 0; y < factor; y++) { b->setPixelDP( (px + x2) * factor + x, (py + y2) * factor + y, 0xFF00FF00); } } } } } } } } screen->render(); lastX = px; lastY = py; } } return true; }); z->setTastaturEreignis([&size, screen, tf, this, charMask, x, y, width, height, factor, b](void* z, void* p, Framework::TastaturEreignis te) { if (te.id == Framework::TE_Press && te.taste[0] == '+') { size++; } if (te.id == Framework::TE_Press && te.taste[0] == '-' && size > 1) { size--; } if (te.id == Framework::TE_Press && te.virtualKey == 'I' && Framework::getTastenStand(Framework::T_Strg)) { if (!tf->zText()->hat("")) { tf->setText(Framework::Text("") + tf->zText()->getText() + ""); } } if (te.id == Framework::TE_Press && te.virtualKey == 'U' && Framework::getTastenStand(Framework::T_Strg)) { if (!tf->zText()->hat("")) { tf->setText(Framework::Text("") + tf->zText()->getText() + ""); } } if (te.id == Framework::TE_Press && te.virtualKey == 'B' && Framework::getTastenStand(Framework::T_Strg)) { if (!tf->zText()->hat("")) { tf->setText(Framework::Text("") + tf->zText()->getText() + ""); } } if (te.id == Framework::TE_Press && te.taste[0] == ' ') { bool hasAny = 0; for (int x = 0; x < charMask->getWidth(); x++) { for (int y = 0; y < charMask->getHeight(); y++) { if (charMask->getMask()[y * charMask->getWidth() + x]) { hasAny = 1; } } } if (!hasAny) { bool first = 1; for (int xx = x; xx < x + width && first; xx++) { for (int yy = y; yy < y + height && first; yy++) { if (mask[yy * this->width + xx] && (yy - y) < charMask->getHeight() && (xx - x) < charMask->getWidth()) { if (first) { charMask ->getMask()[(yy - y) * charMask->getWidth() + (xx - x)] = 1; } first = 0; } } } } bool changed = 1; while (changed) { changed = 0; for (int xx = x; xx < x + width; xx++) { for (int yy = y; yy < y + height; yy++) { if (mask[yy * this->width + xx] && yy - y < charMask->getHeight() && xx - x < charMask->getWidth() && !charMask ->getMask()[(yy - y) * charMask->getWidth() + xx - x]) { bool hasNeighbor = 0; for (int i = -1; i <= 1 && !hasNeighbor; i++) { for (int j = -1; j <= 1 && !hasNeighbor; j++) { if (yy - y + j >= 0 && yy - y + j < charMask->getHeight() && xx - x + i >= 0 && xx - x + i < charMask->getWidth()) { hasNeighbor |= charMask->getMask() [(yy - y + j) * charMask ->getWidth() + xx - x + i]; } } } if (hasNeighbor) { charMask ->getMask()[(yy - y) * charMask->getWidth() + xx - x] = 1; changed = 1; } } } } } for (int i = 0; i < width * factor; i++) { for (int j = 0; j < height * factor; j++) { if (mask[(j / factor + y) * this->width + i / factor + x]) { if (charMask->getMask()[(j / factor) * charMask->getWidth() + i / factor]) { b->setPixelDP(i, j, 0xFF00FF00); } else { b->setPixelDP(i, j, 0xFFFFFFFF); } } } } tf->addStyle(Framework::TextFeld::Style::Fokus); } screen->render(); return te.taste[0] == ' '; }); while (wait) { Sleep(100); } if (!aborted) { charMask->getResult().setText(tf->zText()->getText()); addChar(charMask); saveChars(); } else { delete charMask; } screen->lock(); screen->removeMember(tf); screen->unlock(); return !aborted; } bool hasImage() const { return image; } void writeToFile(Framework::Datei& dat) { dat.schreibe(result.getText(), result.getLength()); } };