Skip to content

Image Loading

Gerbesh edited this page Oct 8, 2026 · 1 revision

Image Loading

OPBrowser supports initial <img src> loading for PNG, JPEG, GIF and BMP. Files, HTTP(S) resources and data:image/... URLs feed the same owned resource pipeline. Only the first frame is decoded; GIF animation is not implemented.

Ownership and source policy

The HTML parser already decodes references in src/href/alt attributes. After a document loads, op_engine::images walks image nodes outside hidden head/title/style/ script/template subtrees. Relative sources use the effective loaded document address, including redirects. Local image paths resolve beside the loaded file; local pages can also use explicit file URLs/drive paths. Data documents have no relative base, but can contain data or absolute HTTP(S) image sources.

Network pages cannot request local files. HTTPS pages reject HTTP image downgrades; WinHTTP also rejects HTTPS-to-HTTP redirects. Cookies/automatic authentication stay disabled. Missing Content-Type or application/octet-stream permits supported raster signature detection; other HTTP types must be PNG/JPEG/GIF/BMP. HTML responses are rejected. Image data URLs accept percent encoding or base64; unsupported parameters or types fail. These policies are distinct from page-link navigation.

op_net owns bounded binary transport. op_image owns immutable, validated top-down premultiplied BGRA RasterImage buffers. Targeted windows 0.62.2 bindings call explicit Microsoft WIC decoders for the supported container signatures. WIC only handles the raster codec, not HTML, DOM, CSS, layout, painting or Web APIs. See Microsoft's WIC overview. COM interfaces remain local to decoding and are released before balancing COM initialization. Only owned pixels cross the worker/UI channel.

Bounds and lifecycle

  • At most 32 visible DOM/generated image candidates receive resource processing per page, sharing one budget. Computed display:none subtrees and suppressed pseudos are skipped.
  • At most eight distinct resolved sources are attempted, including failed loads.
  • Each encoded image is limited to 4 MiB; the page accepts at most 8 MiB of successfully returned encoded resources. Partial failed responses are bounded by the per-request limit and attempt count, not added to that accepted-byte budget.
  • Decoded images allow at most 4096 pixels per dimension and 4,194,304 total pixels (16 MiB BGRA). Width/height/budget checks occur before pixel allocation/copy.
  • Unique raster buffers together are limited to 32 MiB per page. Repeated successful sources share Arc pixels; failed source results are cached within the page too.
  • New source attempts stop after ten elapsed seconds; image HTTP operations use two-second timeouts and check a five-second elapsed deadline between body reads. These checks are not a hard total wall-clock cap on synchronous OS operations or codec work. Images load serially before the worker publishes the page.

Navigation/reload builds a fresh per-page cache; there is no persistent image cache. The active page retains its successful per-node Arc resources for Page Reflow; resizing performs no image requests or decoding. Old/new display lists can coexist while the worker prepares a replacement, so the page budget is not a total-process memory limit. GDI temporarily copies one source raster into a DIB for each visible paint; codec/OS working memory is separate from the owned buffer limits. No image timers or animation CPU run in the background.

Layout, fallback and native drawing

Images share measured lines with text, align their bottom edge to the text baseline and wrap as atomic boxes. See Inline Layout. Natural sizes are used unless CSS sizes or supported integer width/height attributes override them. Attributes supply computed hints before author cascade, so CSS auto/global resets override them. Percentage widths use containing content width; font-relative units use computed font size. Definite heights, min/max sizes and content-box/border-box sizing are supported. One dimension preserves aspect ratio; two dimensions may stretch. Both-auto min/max constraints preserve ratio where compatible; conflicting constraints may stretch and minima win over smaller maxima. Percentage heights remain auto-like pending containing-height propagation. Oversized attribute values above 4096 are ignored; zero final dimensions suppress drawing/fallback. Existing viewport fitting can shrink below CSS minimum sizes. Boxes shrink proportionally to fit content width and cap displayed height at 4096. DOM img padding/background/solid borders form atomic inline boxes around those content dimensions. Width fitting reserves edge space, the border-box bottom aligns to baseline, and nowrap keeps image boxes on the current line. Image hit regions cover the raster content. Surrounding text retains order; unavailable/blocked/over-budget images with nonempty alt use styled text fallback. Empty/absent alt uses transparent replacement geometry around zero natural dimensions. CSS sizes/min/max/padding/borders still apply, without a fake raster or label. An image failure does not fail document navigation or roll back history.

ImageBox and Image paint commands carry Arc pixels and inherited anchor hrefs. The native backend uses a transient selected DIB/DC and GDI AlphaBlend with per-pixel alpha, then restores/releases GDI objects. Fully clipped images are skipped. Image rectangles use the existing toolbar/scroll-aware click/cursor hit testing and clear when the display list is replaced.

An initial bounded SVG image slice is supported before the platform raster decoder: root width/height and viewBox provide intrinsic width/height/ratio metadata, and simple <rect> content is rasterized into the same premultiplied BGRA resource used by ordinary images. The parser enforces the existing encoded/pixel/dimension budgets plus a rectangle-count cap. This is not a general SVG renderer: paths, text, transforms, gradients, filters, CSS/styling and broader SVG semantics remain future work.

Progressive loading, animation, srcset/picture, full loading-state/quirks-mode fallback semantics, full percentage-height propagation, WebP/AVIF, EXIF orientation and color management also remain future work.

CSS generated image content

Before/after content accepts quoted/unquoted url() mixed with strings, attr/counter values and quotes. ComputedPseudoStyle.items retains text/image order and the consuming style_node. External URLs resolve relative to the effective stylesheet address after redirects; embedded and inline source bases use the document. Custom URLs stay unresolved until consumed by a normal content declaration, so var() uses the consuming stylesheet's base. References: generated image values, relative URLs in custom properties.

PageImages retains generated Arc resources by (host NodeId, pseudo, item index) beside DOM images. Both share the cache and all bounds above; failed URLs remain cached. Generated images use intrinsic sizes, shrink to available content width, share normal text baselines, wrap atomically and can enter generated block flow. They inherit anchor href for native clicks. Unavailable images in mixed lists add no image or alt label; their surrounding text/empty pseudo decorations still render. Image modifiers/gradients and alternative-text content syntax remain future work. In-memory render_html/set_html_page still perform no subresource loading; use source navigation for images.

A content declaration consisting of exactly one URL marks an image replacement before text materialization. Inline image pseudos share DOM img CSS size/box-sizing/min/max/ratio/fitting and their own padding/background/border geometry. A URL beside even an empty string or quote action remains an anonymous image in a content list. Missing sole-URL replacements have zero natural dimensions and no ratio: CSS axes resolve independently, including zero content axes with nonzero padding/borders. Optional-pixel InlineImage carries geometry without generating ImageBox/paint/hit regions. Native hit regions continue to cover available raster content pixels. References: invalid CSS image replacement, HTML image fallback.

Inline DOM/sole-URL images retain decorated ancestor stacks and a separate atomic own box. Anonymous images in mixed pseudo lists remain inside the pseudo/host fragments. Available width fitting reserves all ancestor edges; CSS percentage widths still use the containing block width. Outer backgrounds paint before inner image decorations and shared raster pixels.

Block DOM and sole-URL images share intrinsic/CSS sizing, auto horizontal margins and adjacent vertical margin collapse. Exact padding/border box height advances block flow without extra text-line leading; percent widths retain containing width as their basis while fitting leaves space for specified horizontal margins. Missing replacements share exact block geometry without anonymous line leading; nonempty DOM alt uses ordinary block sizing via BlockContent::ImageAlt.

cargo run -p op_browser -- --image-smoke-test examples/css/generated-images.html
cargo run -p op_browser -- --link-smoke-test examples/css/generated-images.html

Verification

cargo run -p op_browser -- examples/images/index.html
cargo run -p op_browser -- --image-smoke-test
cargo run -p op_browser -- --link-smoke-test examples/images/index.html
cargo run -p op_browser -- --image-smoke-test examples/images/inline.html
cargo run -p op_browser -- --link-smoke-test examples/images/inline.html

The fixtures contain original generated color pixels, not external assets. Native smokes require actual raster painting and a click on the image link. Unit/integration tests cover four codecs, exact PNG premultiplication, oversized/truncated input, data and redirected HTTP images, duplicate/failure caching, byte/pixel/node/attempt budgets, dimensions, alt/history behavior and real GDI colors/alpha/hit regions.

Clone this wiki locally