Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged with KOReader fork of crengine #125

Merged
merged 36 commits into from Jan 27, 2020
Merged

Merged with KOReader fork of crengine #125

merged 36 commits into from Jan 27, 2020

Conversation

pkb
Copy link
Contributor

@pkb pkb commented Jan 18, 2020

This PR should close #91. It includes a lot of enhancements in css processing, thanks to koreader devs and especially to poire-z for work on crengine.

It wasn't that easy to merge, in case of conflicts I mostly accepted their side. Except for kerning mode setting, We handle it differently - we use a combination of new setting "Text shaping mode" and the old Kerning enable/disable.

They have added qimagescale from QT, I moved it to thirdpatrty folder. I also added nanosvg library to the thirdparty folder, harfbuzz I updated to 2.6.4 version.

Note: this change is not yet ready for Android, @virxkane will create a separate request for it.

pkb and others added 23 commits January 4, 2020 12:19
- Delay computation of alignment when vertical-align: top or bottom,
  as we need the full line to be laid out to know the final line
  height and baseline.
- Also make the image vertical alignment code not assuming that
  baseline_to_bottom=0 (even if it's true for images), so it's
  ready to be re-used when implementing "display: inline-block"
  (where the baseline is not the bottom of the box).
- Fix half_leading by setting half_leading_bottom, as otherwise
  1px could be lost, causing possible alignment issues (but
  actually not anymore with the added delayed computation).
Floats among floats should be cleared and shouldn't overflow
the top float box (they were overflowing its margin bottom).
Also, when they are cleared, the space cleared should not
be removed from the bottom margin, so it's fully shown.
Also fix uninitialized value (reported by clang-tidy).
Generic support for baseline of blocks. Will be needed to
support "display: inline-block" (boxes whose baseline is
the baseline of the last line box it contains) and
"inline-table" (boxes whose baseline is the baseline of
the first line box, or the bottom of the first table row).
- FlowState::addContentLine() takes an additional parameter,
  that we set when adding a line (ignored when adding paddings
  and margins).
- renderBlockElement() takes an optional *baseline so callers
  can get it additionally to the returned height.
- Also allows storing a baseline in RenderRectAccessor.
- Enhanced Block Rendering: add a new toggable feature to allow
  rendering correctly an element with display: inline-block as
  an inline box (like an image, part of a line).
- initNodeRendMethod() and lvrend.cpp setNodeStyle(): wrap
  elements with display: inline-block or inline-table in an
  internal inlineBox element (mostly just like we wrap an
  element with float: in a floatBox element).
  An inlineBox is erm_inline, and only needs to get the
  vertical-align property of its child.
- renderBlockElementEnhanced(): deal with inlineBox specifically,
  but mostly the same as we handle a floatBox element.
- getRenderedWidths(): recursive call to measure an inlineBox
  while measuring its containing final block.
- renderFinalBlock(): add an inlineBox to the LFormattedText
  object the same way we add images.
- lvtextfm: handle inlineBox mostly as we handle images (same
  vertical alignment code), and render them when measuring
  text to get their width, height, and baseline. Store their
  size and position in their RenderRectAccessor (once the words
  are aligned, and their position on the line known).
  Move some code from the float drawing section in an added
  getAbsMarksFromMarks() function, as we need it too with
  inlineBoxes when drawing selected text.
- ldomDocument::createXPointer(pt): allow recursive call,
  used when pt is inside an inlineBox or a floatBox (former
  code for floatBox replaced by this cleaner way).
crengine native text selection (and internal bookmarks)
highlighting was expecting single-column full-width
paragraphs with nothing else on the line, and may be ugly
when floats, table cells, or RTL/BiDi text are involved.
This adds an alternative method for drawing them by using
getSegmentRects(), and getting multiple small rects that
will each compose the final highlighting.
It can be toggled by using ldomXRange.setFlags(2) on the
range to highlight (the original method will still be
used when using ldomXRange.setFlags(1)).
Also avoid division by zero (reported by clang-tidy).
@poire-z
Copy link
Contributor

poire-z commented Jan 18, 2020

I see you haven't brought our epub.css, which might be fine, dunno.
But you should be aware of some other related changes I made that may cause a risk to your long time users : I made some changes to the default display: block / inline of some elements (in fb2def.h, and in our epub.css), and this might change the DOM tree - and so, xpath/xpointers that your users may have saved might not be found in the new DOM.
Some comments about that in: https://github.com/pkb/coolreader/blob/eccef73becff11af69a27dd763cf4991385b7cda/crengine/src/lvtinydom.cpp#L15-L65
And in koreader/crengine#172 koreader/crengine#192 koreader/crengine#194 (and possibly others...)

On KOReader side, we save a cre_dom_version setting, and previous books opened with an older version have koreader request a specific DOM version handling from the code, in the hope old xpointers will be found in a DOM tree built with new tweaks/fixes reverted.
This also affected our epub.css, that has this ugly section at its bottom:
https://github.com/koreader/crengine/blob/f20454111fff249d78a1f092ff7ccba52c8b720f/cr3gui/data/epub.css#L170-L207

I remember that I have thought a few times "they will have to take care of that if it's brought back into upstream" - but right now, I can't remember the specific points, except the one above.
But I usually tried to keep upstream (your :) behaviour as the default (often mentionned as "legacy" in the code), and our enhancements are enabled from our frontend - via some of the added PROP_* and some other possibly via specific added methods (but I can't find any in lvdocview.cpp :|) or via added parameters to existing methods, keeping the legacy behaviour with the default argument for the added parameter.
For example, float support (that may break xpath/xpointers) is enabled via PROP_RENDER_BLOCK_RENDERING_FLAGS "crengine.render.block.rendering.flags" - which should default to 0 (legacy rendering), but if you want to enable floats and the other block rendering flags, you'll have to add settings/toggles to your various frontends.

I can't remember about all the things I added over the last 2 years and which may affect you - but the PRs I opened in koreader/crengine, and my commit messages, are usually quite verbose :) so you might want to spend some time browsing thru them to see what you get with this merge (possible issues, or enhancements that need to be enabled from frontends).

Another (a lot more concise :) source for what's available for your frontends might be the history of our single-file crengine interface: https://github.com/koreader/koreader-base/commits/master/cre.cpp

TLDR: be careful, and test a lot before making a release with this :)

@Frenzie
Copy link

Frenzie commented Jan 18, 2020

Having the two come closer together is a welcome and doubtless quite time consuming endeavor. :-)

@pkb
Copy link
Contributor Author

pkb commented Jan 19, 2020

But you should be aware of some other related changes I made that may cause a risk to your long time users :

@poire-z thank you very much for bringing this to our attention.

@poire-z
Copy link
Contributor

poire-z commented Jan 20, 2020

Sharing more thoughs (as they are visiting me:) about things you may want to check/look at:

I believe we could get rid of this, as it looks to me it's for CoolReader behaviour: with CoolReader, we may change from the UI various settings, including styles, so that's crengine way of knowing some things have changed and that the rendering needs to take them into account.
With KOReader, as for each typeset/layout change, we are explicitely calling methods on crengine to set them, before rendering, we may not need that section.

  • in KOReader, we always have only one single CRE document loaded. This has allowed me to take shortcuts and use some cheap global variables for some settings (global render dpi for sizing CSS Adds support for dpi-based css units (px/in/cm/mm/pt/pc) koreader/crengine#213, global interline scale factor line-height: reworked implemenation for better conformance koreader/crengine#273...). I believe CoolReader can have multiple documents opened, and so, changing these settings on a document will have it changed for all other opened documents, meaning some style hash change that will cause a re-rendering and cache update.
    I think this was already the case for the floating/hanging punctuation toggle that was there before I joined in, as you have that bool gFlgFloatingPunctuationEnabled. So, if you can toggle that from your frontends' UI, you should meet the situation I describe. Up to you to see if that's an issue or not.
    Might not be an issue if you hardcode one value and don't allow for frontends to tune it, but well :)

@pkb
Copy link
Contributor Author

pkb commented Jan 20, 2020

I believe CoolReader can have multiple documents opened

No, it can switch to recently opened document but that's all. CoolReader is a single document app.

@poire-z
Copy link
Contributor

poire-z commented Jan 20, 2020

No, it can switch to recently opened document but that's all. CoolReader is a single document app.

Oh, allright. The lvtinydom code allows for multiple document instances. And I just remember we reduced the max number of such instances from 256 to 16, to allow for more nodes and bigger books (koreader/crengine#121 (comment), koreader/crengine#254).

convertBookmarks = historyRecord->getBookmarks().length() > 1;
}
} else
gDOMVersionRequested = gDOMVersionCurrent;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Been wondering how much of that CRFileHistRecord stuff in that LVDocView::LoadDocument() I can keep without risk for KOReader, as we don't use CRFileHistRecord, so all that stuff should be no-op.
Is that the case too with CR on Android?
But this line bothers me: needToConvertBookmarks() is called even if there's no CRFileHistRecord (meaning all that is managed from outside), so it looks like this else is wrongly killing the gDOMVersionRequested the outside code may have requested?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we change needToConvertBookmarks() like below:

diff --git a/crengine/src/lvdocview.cpp b/crengine/src/lvdocview.cpp
index d2ea6dc..ff7928e 100644
--- a/crengine/src/lvdocview.cpp
+++ b/crengine/src/lvdocview.cpp
@@ -3751,13 +3751,12 @@ static void FileToArcProps(CRPropRef props) {
 static bool needToConvertBookmarks(CRFileHistRecord* historyRecord)
 {
     bool convertBookmarks = false;
-    if(historyRecord) {
+    if(historyRecord && historyRecord->getBookmarks().length() > 1) {
         gDOMVersionRequested = historyRecord->getDOMversion();
         if(gDOMVersionRequested < 20180528) {
-            convertBookmarks = historyRecord->getBookmarks().length() > 1;
+            convertBookmarks = true;
         }
-    } else
-        gDOMVersionRequested = gDOMVersionCurrent;
+    }
     return convertBookmarks;
 }

This way it will not affect you, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks fine. (So, that way, you're not resetting at all gDOMVersionRequested , but it will be gDOMVersionCurrent when you have no historyRecord because... you set/reset it to gDOMVersionCurrent elsewhere?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially gDOMVersionRequested is equal to gDOMVersionCurrent. I did not find were is it changed. I assume it is changed in a frontend or I missed something. So the one who changes gDOMVersionRequested is responsible for resetting it.

I did some more changes, please take a look: https://gist.github.com/pkb/64714978e3a467b687b738c3a0f3b51b

Does it make sense?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the one who changes gDOMVersionRequested is responsible for resetting it.

OK, In KOReader, we set it from frontend before each book loading (from the value saved in our book settings, or to the gDOMVersionCurrent that we have fetched from CRE, when the book has no setting yet).
I get that in your case, for each book, you set it first to the value saved in your CRHistFile (so, the oldest if none), always migrate bookmarks and always end up setting it to gDOMVersionCurrent. So I guess both you and me are fine (as long as you don't tweak gDOMVersionRequested when there is no CRHistFile :)
Your patch looks sane to me, when reading it in your and my contexts.

But I don't know how that work for CR Android, You told me bookmarks were saved in a SQLite DB, so I assume it's not using CRHistFile, right?. Is that gDOMVersion business and bookmarks migration taken care in any way on CR Android?

Also, a question, just because I don't know much about CRHistFile: is it always fed from outside frontends? So, if that is not done by the frontend, it's always NULL and checks with it are just skipped, right?

Btw, in your merged code and patch, you might want to use (instead of your hardcoded < 20180528):

#define DOM_VERSION_WITH_XPOINTERS_V2 20180528
or
#define DOM_VERSION_WITH_NORMALIZED_XPOINTERS 20180528

(because for me, that won't be 20180528, but something like 20200214, depends on when I'll get that ready :) or you could have used anything 2020xyxz that I could reuse - but minor diffs, no problem).

historyRecord->getBookmarks().length() > 1 - another (less critical) thingy that might need conversion is the last position in book. After migration and switch to the latest DOMVersion, the last position might not resolve - unless crengine handles that somehow (I've seen it doing that in some other contexts, like re-renderings, but not sure if it uses a xpointer or a y-coordinate).

(In CRFileHistRecord::convertBookmarks(), your toggling of gDOMVersionRequested between old and new versions is just to pick the right version of createXPointer() and toString(), right? would have worked the same by using createXPointerV1 and toStringUsingNames(New), right?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know much about Android part. Let's wait and see what @virxkane has to say about it.
AFAIU new CRFileHistRecord gets created whenever LVDocView::savePosition() is called. So you might have a record if you call LVDocView::goLink() for example. It will not be saved to a file, so on start record list will be empty. But anyway getBookmarks().length() will be at most 1 so

Btw, in your merged code and patch, you might want to use

Ok

another (less critical) thingy that might need conversion is the last position in book

Yes I'm aware of this. Maybe I'm wrong but I've decided to skip this conversion. There is a chance that reading position will be lost.

In CRFileHistRecord::convertBookmarks(), your toggling of gDOMVersionRequested

Right.

@virxkane could you please check the patch https://gist.github.com/pkb/64714978e3a467b687b738c3a0f3b51b and apply it along with your other changes if you find it ok? Thanks

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pkb Ok, it's done. https://github.com/virxkane/coolreader/commit/999a484ee1f9c4a9287932537fae9697f96f7d28
@poire-z About your question: yes, on Android bookmarks saved in SQLite DB, so CRHistFile is unused. Bookmark migration on Android for now not implemented yet. In latest commit https://github.com/virxkane/coolreader/commit/3189393b1067ddab0b33375ebbf1f40014258edb I implement saving & loading DOM version of each document in DB, so at least old bookmark must be handled properly. In the future, the conversion of bookmarks will probably be implemented.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some update from my side: I brought these normalize xpointers in koreader/crengine#328 - with some additional minor tweaks and renames.

AFAIU new CRFileHistRecord gets created whenever LVDocView::savePosition() is called. So you might have a record if you call LVDocView::goLink() for example.

Ok, we're indeed using that, only when resizing or changing orientations. Didn't take any risk and let your CRHistFile conversion code out.

@poire-z
Copy link
Contributor

poire-z commented Feb 23, 2020

Another thing I forgot and that I should mention:
The default caches size in lvtinydom.cpp are really small. I did not dare updating them in the source file, but I added stuff so frontend code can feed a factor to have them get bigger. We currently pass a factor of 20, and I'm going to update it to 40.
Reaching the limits on these caches is the cause of other obscure bugs and slowness. Details in:
koreader/crengine#100 - koreader/koreader#3700 - koreader/koreader#3732

@virxkane
Copy link
Collaborator

virxkane commented Mar 7, 2020

@poire-z

Also, when testing your cr3.2.39 apk with a big book (not free, can't share, but any christian Bible would do I guess :), page turn is really slow (5 to 10 seconds, might be just node styles hash recomputing, or a hidden reformating), and reformatting (with progress bar) happen on load (so, like a cache hash mismatch).
So, you might have some issue with hash/style hash/display hash checks on page turn :( (I got that a few times, and it had always been painful to debug...) May be you'll have some CRE WARNING: in your output that could give some (limited) hints?
May be it's related to your stylesheet injection/check on each page turn, and the optimisation I mentionned in the 2nd point of #125 (comment). Or not.

(I do think your current code before this merge did have some cache/hash bugs that would cause some reparsing/reformatting on load - but you should have benefit from my fixes to that with this merge - except that your style injection is different than ours, and it may bring some new issues...)

I found root of this problem. I think it's affected only Android version. Method org.coolreader.crengine.DocView.getPositionPropsInternal() call LVDocView::getBookmark() & LVDocView::getPageDocumentRange(int), which your team greatly modified and they became really really slower.
LVDocView::getPageDocumentRange(int):

    ...
    for (start_h=0; start_h < height; start_h++) {
        start = m_doc->createXPointer(lvPoint(0, start_y + start_h), 1);
        // printf("  start (%d=%d): %s\n", start_h, start_y + start_h, UnicodeToLocal(start.toString()).c_str());
        if (!start.isNull()) {
            // Check what we got is really in current page
            lvPoint pt = start.toPoint();
            // printf("start pt.y %d start_y %d end_y %d\n", pt.y, start_y, end_y);
            if (pt.y >= start_y && pt.y <= end_y) {
                found = true;
                break; // we found our start of page xpointer
            }
        }
    }
    ...

LVDocView::getBookmark()

	...
	for (int y = page->start; y < page->start + page->height; y++) {
		// Use direction=1 to avoid any x check (in case there
		// is some left margin)
		ptr = m_doc->createXPointer(lvPoint(0, y), 1);
		lvPoint pt = ptr.toPoint();
		if (pt.y >= page->start) {
			if (!fallback_ptr)
				fallback_ptr = ptr;
			if ( pt.y < page->start + page->height ) {
				// valid, resolves back to same page
				found = true;
				break;
			}
		}
	}
	...

Are these cycles really useful and necessary? I tried to increase the increment of the loop counter to 10, 100, it works much faster. But there is a risk of not finding anything at the end of the desired interval... What is the preferred increment value of the loop counter in your opinion? Or may be use Bisection method? Or something else?

@poire-z
Copy link
Contributor

poire-z commented Mar 7, 2020

Are these cycles really useful and necessary?

Well, usually, the for loops should not go on much, there's always a break that should get out of it really early.
It was needed when there is some top margin or some space at top, that would not resolve to any node: so, when it does not, try top+1px top+2ps... hopefully, top + a few pixels should resolve to a node, and we get out of it. So, really, the increment should be 1. If you jump by too large, you may miss some nodes (which doesn't matter for getBookmarks, but it may with getDocumentRage (full text search).
Some link to past issues at the bottom of the last PR about it: koreader/crengine#313

But also see #125 (comment) just above - and try to increase these cache sizes. It's possible these affect createXPointer(lvPoint) as it needs to go thru many RenderRectAccessor - and if they can't get cached, there might be some recomputing that would cause some slowdown.

I think it's affected only Android version. Method org.coolreader.crengine.DocView.getPositionPropsInternal() call LVDocView::getBookmark() & LVDocView::getPageDocumentRange(int)

Can't really find out how that works, and where you need that and how often you call it... Android source tree is quite obscure to me :)
So, why/how often?

@virxkane
Copy link
Collaborator

virxkane commented Mar 8, 2020

Can't really find out how that works, and where you need that and how often you call it... Android source tree is quite obscure to me :)
So, why/how often?

On a smartphone with page animation turned on - 9 times per page turn.

(java code) ReaderView.animatePageFlip() -> 
  (java code) DocView.getPositionProps() -> 
    (cpp  code) DocView.getPositionPropsInternal()
(java code) ReaderView.animatePageFlip() ->
  (java code) ReaderView.PageViewAnimation class -> 
    (java code) ReaderView.preparePageImage() ->
      (java code) DocView.getPositionProps() -> 
        (cpp  code) DocView.getPositionPropsInternal()

The latter is executes few times.

On e-ink bookreader (without animation) - 2 times per page turn.

(java code) ReaderView.doEngineCommand() -> // cmd=DCMD_PAGEUP
  (java code) ReaderView.updateCurrentPositionStatus() ->
    (java code) DocView.getPositionProps() -> 
      (cpp  code) DocView.getPositionPropsInternal()
(java code) ReaderView.DrawPageTask class -> 
  (java code) ReaderView.preparePageImage() ->
    (java code) DocView.getPositionProps() -> 
      (cpp  code) DocView.getPositionPropsInternal()

Increasing the maximum allowed cache size not fixed this slowness. But I found integer overflow in cache size calculations :)

@poire-z
Copy link
Contributor

poire-z commented Mar 8, 2020

For info, with KOReader, we do not use any of the CMD stuff.
I think a few times I made some changes, I noticed there would be needed also in some CMD part of the code - but as I couldn't test any change there, I didn't touch these... (and of course, I can't remember which :|)

Anyway, in your samples above, if for a page turn, wouldn't they return always the same value, and would benefit being cached locally so you don't call them 9 times?

@poire-z
Copy link
Contributor

poire-z commented Mar 11, 2020

Increasing the maximum allowed cache size not fixed this slowness. But I found integer overflow in cache size calculations :)

Did your fix (https://github.com/virxkane/coolreader/commit/36299e6e14) and increasing cache size help with the slowness?

(I noticed some slowness in some page turn after I added a new feature, that resulted in the for loop iterating all over the height - it had to be fixed elsewhere koreader/crengine@1cec90fbc4 . So, if you really notice the for loops being done fully, it might be a bug - hopefully only on some specific pages of some specific book, but not all?)

@virxkane
Copy link
Collaborator

Did your fix (virxkane@36299e6) and increasing cache size help with the slowness?

No, it not helps. This commit for future increasing cache that has not yet been applied.

hopefully only on some specific pages of some specific book, but not all?)

Yes, the slowdown is not on all books, I hope your fix corrects this behavior.
Unfortunately, there is not enough time, but today I’ll try to play with page turn some more.

@poire-z
Copy link
Contributor

poire-z commented Mar 12, 2020

I hope your fix corrects this behavior.

Possibly not, that fix was for taller-than-page elements with display: inline-block, which must be quite rare.
Anyway, if you have some specific book (and page reference) where you notice the slowdown, and you can upload that book, I can have a look (to at least see if that slowdown is noticable with KOReader, and if yes, I'll investigate).

@virxkane
Copy link
Collaborator

@poire-z DocumentsForTestingRTL.zip that you upload about month ago, file Petra.AR.epub. Slowdown occurs when I turn pages around contents with numeric list.

@virxkane
Copy link
Collaborator

@poire-z Forgot to add: with DOMVersion and BlockRenderingFlags both equals 0.

@poire-z
Copy link
Contributor

poire-z commented Mar 12, 2020

OK, took that epub from that zip file. With KOReader, and just adding:

ldomXPointer LVDocView::getBookmark() {
        LVLock lock(getMutex());
        checkPos();
        ldomXPointer ptr;
        if (m_doc) {
                if (isPageMode()) {
                        if (_page >= 0 && _page < m_pages.length()) {
                                // In some edge cases, getting the xpointer for y=m_pages[_page]->start
                                // could resolve back to the previous page. We need to check for that
                                // and increase y until we find a coherent one.
                                // (In the following, we always want to find the first logical word/char.)
                                LVRendPageInfo * page = m_pages[_page];
                                bool found = false;
                                ldomXPointer fallback_ptr;
                                for (int y = page->start; y < page->start + page->height; y++) {
+                                       printf("page.start = %d : y=%d\n", page->start, y);
                                        ptr = m_doc->createXPointer(lvPoint(0, y), PT_DIR_SCAN_FORWARD_LOGICAL_FIRST);
                                        lvPoint pt = ptr.toPoint();

In enhanced mode, I always see a single loop iteration (ok, twice because we must call this twice per page turn, one to go to, one to get the xpointer to restore to):

page.start = 24575 : y=24575
page.start = 24575 : y=24575
page.start = 25127 : y=25127
page.start = 25127 : y=25127
page.start = 25674 : y=25674
page.start = 25674 : y=25674

Still a single one on the TOC at start, and on the footnotes at end.

There's one page when I get 100 loops:

page.start = 15742 : y=15742
page.start = 15742 : y=15743
page.start = 15742 : y=15744
page.start = 15742 : y=15745
[...]
page.start = 15742 : y=15845
page.start = 15742 : y=15846
page.start = 15742 : y=15847
page.start = 15742 : y=15848

Confirmed with an other LTR EPUB that this happens when some float is split among 2 pages, on the 2nd page:
1st page:
img1
2nd page:
img2

It looks logical: when you ask what xpointer is at top of that 2nd page, it gives you the float (or the image inside the float) that starts on previous page, you resolved it back to a y, which happens to be smaller than the one you asked for, so it is bad: so, you continue the loop, until you pass 100px (the height of the 2nd part of the image on that 2nd page), and you finally reach some node that is on this page (y > the y you asked for), and it's fine.
(^ that's to show you the purpose of that loop :) and to refresh my memory :)

In "legacy" mode, I notice all is fine, in the TOC and footnotes, BUT not at end, after the footnotes, when there is a table: there, in that table, there are many loops (but 100 to 200 px only). But well, legacy mode :) not worth investigating.

So, I don't notice what you notice on page with numeric list (TOC at start or footnotes at end?).
Do you notice it on LTR EPUBs too? or only that RTL one?

Some possibly related commits I made that you haven't picked yet, that may be worth quick applying/testing to see if they solve your issue?
koreader/crengine@7ed1ad5
koreader/crengine@343066d
koreader/crengine@204ab8a

@poire-z
Copy link
Contributor

poire-z commented Mar 12, 2020

Forgot to add: with DOMVersion and BlockRenderingFlags both equals 0.

Uh ! that's quite a huge forgetting :)

Well, at minima, DOMVersion should be >= 20180528 (and you do that migration automatically, no?) - so you get at least proper display for elements, and the most essential fixes. Not fixing it as having DOMVersion=20180528 is actually the fix to many things :)
BlockRenderingFlags = 0 = legacy mode, so, some possible minor issues with the table at the end that I noticed above :)

@virxkane
Copy link
Collaborator

@poire-z Ok, I copied all your last commits via cherry-pick and slowdown are fixed now. Thank you.
Before:

preparePageImage(): get currpos: elaped=272

After:

preparePageImage(): get currpos: elaped=12

Logcat at page turn action with some debug noise:

03-12 23:48:12.250 2222-2222/org.coolreader V/cr3: G|rv| onKeyDown(92, KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_PAGE_UP, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=3023151, downTime=3023151, deviceId=-2023406815, source=0x12345678 })
03-12 23:48:12.250 2222-2222/org.coolreader D/cr3: G|rv| onKeyUp: action PAGE_UP found for key 92
03-12 23:48:12.250 2222-2222/org.coolreader I/cr3: G|rv| On command DCMD_PAGEUP (1)
03-12 23:48:12.250 2222-2222/org.coolreader D/cr3: G|rv| doCommand(DCMD_PAGEUP, 1)
03-12 23:48:12.250 2222-2222/org.coolreader D/cr3: G|en| executing task
03-12 23:48:12.250 2222-2248/org.coolreader I/cr3: B|en| running task.work() org.coolreader.crengine.ReaderView$15
03-12 23:48:12.250 2222-2248/org.coolreader V/cr3eng: doCommand(102, 1)
03-12 23:48:12.250 2222-2248/org.coolreader V/cr3eng: getCurrentPageBookmarkInternal: calling getBookmark()
03-12 23:48:12.260 2222-2248/org.coolreader D/cr3eng: new Xpath: /body/DocFragment/body/div/div[2]/div/h2/text().0, old Xpath: /body/DocFragment/body/div/div[2]/div/h2/text().0
03-12 23:48:12.260 2222-2248/org.coolreader V/cr3eng: getBookmarkPosText() : getting position text
03-12 23:48:12.270 2222-2248/org.coolreader E/cr3eng: getPositionPropsInternal(): elapsed=0,0,0,6,3
03-12 23:48:12.270 2222-2248/org.coolreader I/cr3: B|en| exited task.work() org.coolreader.crengine.ReaderView$15. elapsed 17 ms.
03-12 23:48:12.270 2222-2222/org.coolreader I/cr3: G|en| running task.done() org.coolreader.crengine.ReaderView$15 in gui thread
03-12 23:48:12.270 2222-2222/org.coolreader V/cr3: G|rv| drawPage() : submitting DrawPageTask
03-12 23:48:12.270 2222-2222/org.coolreader V/cr3eng: getCurrentPageBookmarkInternal: calling getBookmark()
03-12 23:48:12.270 2222-2222/org.coolreader D/cr3eng: new Xpath: /body/DocFragment/body/div/div[2]/div/h2/text().0, old Xpath: /body/DocFragment/body/div/div[2]/div/h2/text().0
03-12 23:48:12.270 2222-2222/org.coolreader V/cr3eng: getBookmarkPosText() : getting position text
03-12 23:48:12.280 2222-2222/org.coolreader D/cr3: G|en| executing task DrawPageTask
03-12 23:48:12.280 2222-2222/org.coolreader V/cr3eng: getCurrentPageBookmarkInternal: calling getBookmark()
03-12 23:48:12.280 2222-2248/org.coolreader I/cr3: B|en| running task.work() org.coolreader.crengine.ReaderView$DrawPageTask
03-12 23:48:12.280 2222-2248/org.coolreader E/cr3: B|rv| DrawPageTask.work(1072,1448)
03-12 23:48:12.280 2222-2248/org.coolreader V/cr3: B|rv| preparePageImage( 0)
03-12 23:48:12.280 2222-2248/org.coolreader D/cr3: B|rv| BitmapFactory: bitmap released, used size = 0, free size=1
03-12 23:48:12.280 2222-2222/org.coolreader D/cr3eng: new Xpath: /body/DocFragment/body/div/div[2]/div/h2/text().0, old Xpath: /body/DocFragment/body/div/div[2]/div/h2/text().0
03-12 23:48:12.280 2222-2222/org.coolreader V/cr3eng: getBookmarkPosText() : getting position text
03-12 23:48:12.290 2222-2248/org.coolreader E/cr3eng: getPositionPropsInternal(): elapsed=0,1,0,2,3
03-12 23:48:12.290 2222-2248/org.coolreader E/cr3: B|rv| preparePageImage(): get currpos: elaped=12
03-12 23:48:12.290 2222-2248/org.coolreader V/cr3eng: getPageImageInternal entered : bpp=4
03-12 23:48:12.450 2222-2248/org.coolreader E/cr3: B|rv| DrawPageTask.work(): elapsed = 176
03-12 23:48:12.460 2222-2248/org.coolreader D/cr3: B|rv| doDraw() called
03-12 23:48:12.460 2222-2248/org.coolreader D/cr3: B|rv| onDraw() -- drawing page image
03-12 23:48:12.470 2222-2248/org.coolreader I/cr3: B|en| exited task.work() org.coolreader.crengine.ReaderView$DrawPageTask. elapsed 196 ms.
03-12 23:48:12.470 2222-2222/org.coolreader I/cr3: G|en| running task.done() org.coolreader.crengine.ReaderView$DrawPageTask in gui thread

@virxkane
Copy link
Collaborator

virxkane commented Mar 12, 2020

Hmm, no, I found slowdown in other file - Petra.FR.epub
device-2020-03-12-235845
Firstly I go to page on screenshot, then goto next page and:

preparePageImage(): get currpos: elaped=973

Again:

preparePageImage(): get currpos: elaped=1965

As before, the reason is the same - LVDocView::getPageDocumentRange(int), LVDocView::getBookmark().

DOMVersion=last, BlockRenderingFlags=max

@poire-z
Copy link
Contributor

poire-z commented Mar 12, 2020

Can't reproduce on this one.
(Note that there is a large table on the page before - but it shouldn't apply on having a slowdown on going to next page - where I have only text...)

But even with a DOMVersion=20100101 and rend flags=0, I get the image centered. I have it off like you do when I switched from "web" to "legacy" (but koreader tells me the cache is invalid and I need to reload the book - and then I get the image centered).
So, may be clean up your cache files.

And why are you testing with DOMVersion=0/rendflags=0 ? :)

@virxkane
Copy link
Collaborator

And why are you testing with DOMVersion=0/rendflags=0 ? :)

No, DOMVersion=last, BlockRenderingFlags=max.
On koreader this page looks different?

@virxkane
Copy link
Collaborator

Ok, after cleaning cache directory problem fixes. So, on our side incorrectly switched DOMVersion (option in settings dialog), but in other cases not specified, the slowdown may return. This can be a trap.
device-2020-03-13-002900

@poire-z
Copy link
Contributor

poire-z commented Mar 12, 2020

Yep, I have it like that ^ , with a proper float :)

@poire-z
Copy link
Contributor

poire-z commented Mar 12, 2020

So, on our side incorrectly switched DOMVersion (option in settings dialog), but in other cases not specified, the slowdown may return. This can be a trap.

You picked https://github.com/virxkane/coolreader/commit/2aa255072d8e0b8d873759f7ae7454dfc4cf03c9 - so it should be automatically handled: cache trashed when dom version different.
Also see #125 (comment) above, that I just made even more sensitive with https://github.com/virxkane/coolreader/commit/b165b56bc572e0348f6afc2e02b387b41f9bc392. You might want to call after each setting change: include/lvtinydom.h isBuiltDomStale() to either ask the user, or just reload the book - like we do in https://github.com/koreader/koreader/blob/c8b3942bf45ef70b4018b2d1d8272c19d8c0eb57/frontend/apps/reader/modules/readerrolling.lua#L226-L263

@poire-z
Copy link
Contributor

poire-z commented Jan 13, 2021

Just mentionning something I just saw while trying to implement support for CSS min-width/max-height:

In lvtextfm.cpp, there is a resizeImage() function that is supposed to reduce if needed the image size to fit in the paragraph width/page height, but also to apply some of CoolReader options, the Image scaling options.
We don't have these options in KOReader, and never needed to care about them.
But I see that with this PR, you picked modifications from koreader/crengine@9214bfc59#diff-e6da683d521a05f10d1a627822b9d41bbd6646c190f0ec869d779ca27a3c19be (koreader/crengine#35, support for CSS width/height on images, from 2016, before I was involved), that with commenting all the // maxScale = made it always maxScale=1.
And the fact that maxScale=1 makes all CoolReader Image scaling options actually do nothing (well, that's how it looks to me, haven't tried it).

Looks like nobody noticed it since this was merged :) and may be it's just fine.
So, not thinking much about it - but if you were willing to remake it work (we could probably in KR force the image scaling options to do as with maxScale=1) - because how these scaling options should behave vs. CSS styles' width/min-width/max-width is some thinking I don't want to get into :)

@poire-z
Copy link
Contributor

poire-z commented Jan 18, 2021

For info, I restored the maxScale stuff in a commit part of koreader/crengine#409 :

resizeImage(): restore original scaling options code

It was hardcoded'ly removed as part of 9214bfc when
implementing support for CSS width & height for images,
so they don't get in the way.
Removed the hardcoding so the original CoolReader
settings can work again.
We can still disable that from frontend code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Merge with KOReader fork of crengine
6 participants