/************************************************************************ MeOS - Orienteering Software Copyright (C) 2009-2020 Melin Software HB This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Melin Software HB - software@melin.nu - www.melin.nu Eksoppsvägen 16, SE-75646 UPPSALA, Sweden ************************************************************************/ #include "StdAfx.h" #include "animationdata.h" #include "meos_util.h" #include "Printer.h" AnimationData::AnimationData(gdioutput &gdi, int timePerPage, int nCol, int marginPercent, bool animate, bool respectPageBreak) : nCol(nCol), animate(animate), page(-1), gdiRef(0) { lastTime = 0; nextTime = 0; timeOut = timePerPage; doAnimation = true; PageInfo pageInfo; errorState = false; gdi.getTargetDimension(width, height); margin = (width * marginPercent) / 100; double w = (gdi.getWidth() + 20) *nCol + margin; double s = width / w; if ((fabs(s - 1.0) > 1e-3)) { gdi.scaleSize(s, true, false); } pageInfo.topMargin = 20; pageInfo.scaleX = 1.0f; pageInfo.scaleY = 1.0f; pageInfo.leftMargin = 20; pageInfo.bottomMargin = 30; pageInfo.pageY = float(height-margin); pageInfo.printHeader = false; pageInfo.yMM2PrintC = pageInfo.xMM2PrintC = 1; pageInfo.xMM2PrintK = 0; pageInfo.yMM2PrintK = 0; list rectangles; pageInfo.renderPages(gdi.getTL(), rectangles, false, respectPageBreak, pages); } AnimationData::~AnimationData() { if (animationThread && animationThread->joinable()) { animationThread->join(); animationThread.reset(); } if (gdiRef) { gdiRef->removeHandler(this); } } bool AnimationData::takeOver(shared_ptr &other) { delayedTakeOver = other; return true; } void AnimationData::takeOverInternal(shared_ptr &other) { pages.swap(other->pages); width = other->width; height = other->height; nCol = other->nCol; margin = other->margin; animate = other->animate; } void AnimationData::renderPage(HDC hDC, gdioutput &gdi, DWORD time) { bool addTextAnimation = false; if (doAnimation) { if (page == -1) page = 0; else page++; addTextAnimation = true; nextTime = time + timeOut; lastTime = time; gdiRef = &gdi; gdi.addTimeoutMilli(timeOut, "AnimationData", 0).setHandler(this); doAnimation = false; } if (animationThread && animationThread->joinable()) { if (!addTextAnimation) return; // Ignore repaint if (animationThread) { animationThread->join(); animationThread.reset(); } } if (delayedTakeOver && addTextAnimation) { takeOverInternal(delayedTakeOver); delayedTakeOver.reset(); } size_t sp = nCol * page; if (sp >= pages.size()) { sp = 0; page = 0; } int count = 1; for (size_t i = sp; i < sp + nCol && i < pages.size(); i++) { int currentRow = 0; for (auto &text : pages[i].text) { if (text.ti.yp != currentRow) { currentRow = text.ti.yp; count++; } } } int atime = 400; if (count < 30) atime = 800; else if (count < 50) atime = 500; int delay = addTextAnimation && animate ? atime / count : 0; if (delay == 0 || errorState == true) { doRender(hDC, gdi, sp, delay); errorState = false; } else { animationThread = make_shared(&AnimationData::threadRender, this, &gdi, sp, delay); } } void AnimationData::threadRender(gdioutput *gdi, size_t sp, int delay) { HWND hWnd = gdi->getHWNDTarget(); HDC hDC = GetDC(hWnd); int x, y; gdi->getTargetDimension(x, y); RECT rc; rc.left = 0; rc.right = x; rc.top = 0; rc.bottom = y; gdi->drawBackground(hDC, rc); try { doRender(hDC, *gdi, sp, delay); } catch (...) { errorState = true; } ReleaseDC(hWnd, hDC); // End thread and notify that it has ended } void AnimationData::doRender(HDC hDC, gdioutput &gdi, size_t sp, int delay) { for (size_t i = sp; i < sp + nCol && i < pages.size(); i++) { renderSubPage(hDC, gdi, pages[i], margin / 2 + ((i - sp) * width) / nCol, 0, delay); if (i + 1 < sp + nCol) { int x = margin / 2 + ((i + 1 - sp) * width) / nCol - 10; SelectObject(hDC, GetStockObject(DC_BRUSH)); HLS fg, bg; fg.RGBtoHLS(gdi.getFGColor()); bg.RGBtoHLS(gdi.getBGColor()); if (bg.lightness > fg.lightness) fg.lighten(1.3); else fg.lighten(1.0 / 1.3); SetDCBrushColor(hDC, fg.HLStoRGB()); Rectangle(hDC, x - 1, 20, x + 1, height - 20); } } } void AnimationData::renderSubPage(HDC hDC, gdioutput &gdi, RenderedPage &page, int x, int y, int animateDelay) { int ox = gdi.getOffsetX(); int oy = gdi.getOffsetY(); int top = 10000; for (const auto &text : page.text) { if (!text.ti.isFormatInfo()) { top = min(top, text.ti.yp); } } gdi.setOffsetY(-y+top-margin/2); gdi.setOffsetX(-x); int currentRow = 0; for (auto &text : page.text) { if (animateDelay>0 && text.ti.yp != currentRow) { Sleep(animateDelay); currentRow = text.ti.yp; } gdi.RenderString(text.ti, hDC); } gdi.setOffsetY(ox); gdi.setOffsetX(oy); } void AnimationData::handle(gdioutput &gdi, BaseInfo &info, GuiEventType type) { if (pages.size() > size_t(nCol) || delayedTakeOver) { doAnimation = true; gdi.refreshFast(); } gdiRef = &gdi; gdi.addTimeoutMilli(timeOut, "AnimationData", 0).setHandler(this); }