How a Custom C++ Native Rendering Engine Boosted Reading‑App Performance by Over 2%
The article details the design of a cross‑platform C++ rendering engine built for a mobile reading app, explaining its three‑layer architecture, file‑format support, layout and caching mechanisms, security features, and how these technical choices delivered measurable improvements in page views, reading time, and conversion rates.
Background
WebView’s mature DOM and CSS pipeline cannot meet publishing‑grade layout and high‑frequency interaction requirements because of heavy rendering, JIT latency, and kernel fragmentation. To overcome these limits a native cross‑platform rendering engine was built in C++. The engine tightly integrates a custom layout algorithm with the Skia graphics library, controlling the full chain from parsing to rasterisation.
System Architecture Overview
Three‑Layer Design
Cross‑platform Rendering Engine Layer (ReaderSDK)
Middle Abstraction Layer (Swift wrapper)
Business Application Layer (plug‑in architecture)
Each layer exposes abstract interfaces, enabling loose coupling and independent evolution.
Layer 1 – Rendering Engine (ReaderSDK)
Document Parsing : Uses an Expat‑based SAX parser to stream‑parse EPUB containers and HTML files. TXT files are tokenised by TxtBookReader and converted into virtual HTML tags (e.g., HTML_KIND_P, HTML_KIND_H2).
Unified DOM : All inputs are normalised to an HTML DOM tree ( BaseLabel) and then linearised into a BaseElement array for the layout engine.
Layout Calculation : Retrieves glyph metrics via platform APIs or FreeType, applies kerning, performs line‑break detection (including Chinese punctuation rules), and paginates into BasePage objects. Orphan/widow control prevents single‑line paragraphs at page boundaries.
Floating‑Element Handling : Implements a RectSplitter that maintains used rectangles and finds available gaps using a symmetric‑difference algorithm (O(n log n)). Supports auto‑width floats, clear properties, and cross‑page consistency.
Graphics Rendering : Skia creates a SkCanvas with an ARGB32 bitmap. Text is drawn via drawText after style application; images are decoded with libjpeg‑turbo / libpng / libgif, scaled (bilinear or Lanczos), and composited; SVG files are parsed to SkPath and rendered; visual effects (borders, shadows, gradients) use Skia shaders.
Cache System : Multi‑level LRU caches store the DOM, element arrays, layout results ( BasePage), font/glyph bitmaps, and rendered page bitmaps. Cache keys combine chapter ID, style configuration, and page index; caches are invalidated on style changes.
Platform Font Integration : Android bridges to SkFontMgr (parses fonts.xml) and uses matchFamilyStyleCharacter for fallback. iOS uses CoreText and CTFontCopyDefaultCascadeList for cascading fonts. Dynamic weight adaptation is achieved via CTFontCreateCopyWithSymbolicTraits.
Extended Layout Directives
Custom CSS properties prefixed with zh- are parsed into directive bit‑masks and injected into the DOM. During layout they trigger bypasses or algorithm rewrites, enabling features such as viewport‑independent font locking, out‑of‑flow full‑screen illustrations, and virtual cross‑page splits. All directives are processed in a single layout pass, adding zero runtime overhead.
Security & DRM
Control Plane : Business layer negotiates RSA‑based keys; keys are passed as opaque objects to the kernel.
Data Plane : In‑memory decryption is performed with Crypto++ (dynamic IV, zero‑disk write‑back). Decrypted HTML fragments exist only in registers or volatile memory before being parsed.
Rendering Pipeline (Six Stages)
Document loading & container parsing (ZIP virtual file system, OPF/Spine/NCX extraction, DRM map).
Content parsing & DOM construction (SAX → BaseLabel → BaseElement array).
Layout calculation (glyph metrics, line layout, pagination, block & float handling).
Graphics rendering (Skia canvas, text, images, SVG, visual effects).
Cache management (DOM, layout, fonts, page bitmaps).
Interaction handling (touch‑to‑element mapping, selection, anchor navigation).
Only pages that the user scrolls to are parsed and laid out, reducing first‑screen latency from seconds to milliseconds.
Middle Layer – Swift Abstraction
The Swift layer ( ZHCoreReader) wraps the synchronous C++ engine into asynchronous, type‑safe Swift APIs.
BookReader Core Wrapper : Provides async methods such as layout, renderPage, changeElementCSSStyle, and getParagraphInfo. Internally uses LayoutTaskManager for background execution, priority scheduling, and cancellation.
PageStyle Management : Immutable style objects; any change creates a new instance, guaranteeing thread safety. Supports theme switching, font size, line spacing, margins, and colour configuration.
Resource Downloader : Abstract ResourceDownloader protocol handles image and asset download with lazy loading and SDWebImage‑style caching.
Business Application Layer
Plug‑in Architecture
Features are implemented as independent plug‑ins communicating via a message bus (RxSwift). The manager ( ManuscriptPluginManager / BookPluginManager) handles registration, lifecycle, and hot‑swap of plug‑ins.
Key Features
Comment (弹评) System : Uses BookReader.getParagraphInfo() to map a paragraph index to precise page coordinates. Collision detection prevents overlapping bubbles; data are lazily loaded and persisted.
Text Selection & Highlight : Maps screen coordinates to element indices, computes multi‑line selection rectangles, extracts clean text, and stores highlight metadata for cross‑session restoration.
Reading Progress Management : Calculates progress at the paragraph level, stores locally, then syncs to the server with conflict resolution for seamless cross‑device continuation.
Theme Switching : Injects new CSS rules via BookReader.changeElementCSSStyle(), triggers asynchronous re‑layout and re‑render without UI blockage. Image resources are swapped according to the active theme.
Page‑Turn Modes : Vertical scroll (UICollectionView), horizontal flip (UIPageViewController), and simulated page‑curl with custom transitions, all driven by the same rendering backend.
Conclusion
The solution adopts a three‑layer architecture that spans a high‑performance C++ native rendering engine, a Swift‑based abstraction layer, and a plug‑in‑driven business layer. Direct Skia rasterisation and system‑font integration reduce first‑screen time by >60 %, cut memory usage by >40 %, and lower battery consumption by ~30 %. Platform‑specific code accounts for <10 % of the codebase, enabling full C++ reuse on iOS and Android. Decoupled layers, clear interfaces, and plug‑in extensibility provide strong maintainability and rapid feature iteration, forming a solid technical foundation for future enhancements.
Zhihu Tech Column
Sharing Zhihu tech posts and exploring community technology innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
