docs
Loading...
Searching...
No Matches
Surface.h
Go to the documentation of this file.
1///
2/// Copyright (C) 2026 Ultralight, Inc. All rights reserved.
3/// A license is required for commercial use. https://ultralig.ht
4///
5#pragma once
7#include <Ultralight/RefPtr.h>
8#include <Ultralight/Bitmap.h>
10
11namespace ultralight {
12
13///
14/// User-defined pixel buffer surface.
15///
16/// The library uses this to store pixel data when rendering Views on the CPU (see
17/// ViewConfig::is_accelerated).
18///
19/// You can provide the library with your own Surface implementation to reduce the latency of
20/// displaying pixels in your application (Views will be drawn directly to a block of memory
21/// controlled by you).
22///
23/// When a View is rendered on the CPU, you can retrieve the backing Surface via View::surface().
24///
25/// @pre This is automatically managed for you when using App::Create(), if you want to override
26/// Surface or SurfaceFactory, you'll need to use Renderer::Create() instead.
27///
28/// ## Default Implementation
29///
30/// A default Surface implementation, BitmapSurface, is automatically provided by the library when
31/// you call Renderer::Create() without defining a custom SurfaceFactory.
32///
33/// You should cast the Surface to a BitmapSurface to access the underlying Bitmap.
34///
35/// ## Setting the Surface Implementation
36///
37/// To define your own implementation, you should inherit from this class, handle the virtual
38/// member functions, and then define a custom SurfaceFactory that creates/destroys an
39/// instance of your class.
40///
41/// After that, you should pass an instance of your custom SurfaceFactory class to
42/// Platform::set_surface_factory() before calling Renderer::Create().
43///
45 public:
46 virtual ~Surface();
47
48 ///
49 /// Width (in pixels).
50 ///
51 virtual uint32_t width() const = 0;
52
53 ///
54 /// Height (in pixels).
55 ///
56 virtual uint32_t height() const = 0;
57
58 ///
59 /// Number of bytes between rows (usually width * 4)
60 ///
61 virtual uint32_t row_bytes() const = 0;
62
63 ///
64 /// Size in bytes.
65 ///
66 virtual size_t size() const = 0;
67
68 ///
69 /// Lock the pixel buffer and get a pointer to the beginning of the data for reading/writing.
70 ///
71 /// @note Native pixel format is premultiplied BGRA 32-bit (8 bits per channel).
72 ///
73 /// @return Returns a pointer to the beginning of the locked pixel buffer. The buffer remains
74 /// valid until UnlockPixels() is called.
75 ///
76 virtual void* LockPixels() = 0;
77
78 ///
79 /// Unlock the pixel buffer.
80 ///
81 virtual void UnlockPixels() = 0;
82
83 ///
84 /// Lock the pixel buffer for reading/writing (safe version, automatically unlocks).
85 ///
86 /// @return Returns a managed container that can be used to access the pixel buffer
87 /// (LockedPixels::data()). This container automatically unlocks the pixels when it
88 /// goes out of scope.
89 ///
91 Surface* self = this;
92 return LockedPixels<Surface*>(self);
93 }
94
95 ///
96 /// Resize the pixel buffer to a certain width and height (both in pixels).
97 ///
98 /// This should never be called while pixels are locked.
99 ///
100 virtual void Resize(uint32_t width, uint32_t height) = 0;
101
102 ///
103 /// Shift a rectangle of pixels by an offset.
104 ///
105 /// The library calls this during Renderer::Render() when a page scrolls, moving already painted
106 /// pixels instead of repainting them. Afterward, the library repaints only the exposed strip.
107 ///
108 /// The Surface pixel buffer is the source the library paints into. The destination is wherever
109 /// you copy those pixels to display them (eg, a GPU texture or a window). Scroll() always
110 /// shifts the source.
111 ///
112 /// Most implementations do this with one call to ShiftPixels().
113 ///
114 /// @param rect The rectangle to shift (in pixels). This always lies inside the surface,
115 /// and the shift is smaller than the rectangle on each axis.
116 ///
117 /// @param dx The horizontal shift, in pixels (positive moves pixels to the right).
118 ///
119 /// @param dy The vertical shift, in pixels (positive moves pixels down).
120 ///
121 /// @return Return false in most cases. This means the destination was not shifted. The
122 /// library then adds `rect` to dirty_bounds() so you copy the moved pixels from the
123 /// source to the destination again. Return true only in the rare case where you
124 /// shifted the destination by the same offset yourself (eg, you scrolled your
125 /// window or copied within your GPU texture)-- the library then reports only the
126 /// newly exposed strip as dirty.
127 ///
128 /// \parblock
129 /// @note This is called while pixels are locked (between LockPixels() and UnlockPixels()).
130 /// Shift through the pointer you returned from LockPixels()-- do not lock the buffer
131 /// again.
132 /// \endparblock
133 ///
134 /// \parblock
135 /// @note Rows may be padded. Step rows by row_bytes(), never by width * 4.
136 /// \endparblock
137 ///
138 virtual bool Scroll(const IntRect& rect, int dx, int dy) = 0;
139
140 ///
141 /// Shift a rectangle of 32-bit pixels in place.
142 ///
143 /// Use this from Scroll() (and anywhere else you hold a locked pixel buffer) to move the
144 /// pixels of `rect` by `dx` and `dy`; overlapping rows and columns are handled for you. The
145 /// strip the shift exposes keeps its old contents.
146 ///
147 /// @param pixels Pointer to the first row of the buffer (as returned by LockPixels()).
148 ///
149 /// @param row_bytes Number of bytes between rows.
150 ///
151 /// @param rect The rectangle to shift (in pixels, inside the buffer).
152 ///
153 /// @param dx The horizontal shift, in pixels (positive moves pixels to the right).
154 ///
155 /// @param dy The vertical shift, in pixels (positive moves pixels down).
156 ///
157 static void ShiftPixels(void* pixels, uint32_t row_bytes, const IntRect& rect, int dx, int dy);
158
159 ///
160 /// Add a rectangle to the dirty area.
161 ///
162 /// The library calls this after it paints an area of the pixel buffer. The rectangle is clipped
163 /// to the surface and kept as one of up to kMaxDirtyRects dirty rectangles (nearby ones are
164 /// merged), and dirty_bounds() grows to their union, until you call ClearDirtyBounds(). An
165 /// empty or out-of-bounds rectangle changes nothing.
166 ///
167 virtual void set_dirty_bounds(const IntRect& bounds);
168
169 ///
170 /// Get the dirty bounds.
171 ///
172 /// This value can be used to determine which portion of the pixel buffer has been updated since
173 /// the last call to ClearDirtyBounds(). It always lies inside the surface, and it is empty when
174 /// nothing has been painted since the last clear. It is the union of the rectangles
175 /// dirty_rect() lists.
176 ///
177 /// @note The library clears the bounds itself when it resizes the surface, then paints the
178 /// whole buffer, so a resize never leaves you a stale rectangle from the old size.
179 ///
180 /// The general algorithm to determine if a Surface needs display is:
181 /// ```
182 /// if (!surface.dirty_bounds().IsEmpty()) {
183 /// // Surface pixels are dirty and needs display.
184 /// // Cast Surface to native Surface and use it here (pseudo code)
185 /// DisplaySurface(surface);
186 ///
187 /// // Once you're done, clear the dirty bounds:
188 /// surface.ClearDirtyBounds();
189 /// }
190 /// ```
191 ///
192 virtual IntRect dirty_bounds() const;
193
194 ///
195 /// Clear the dirty bounds.
196 ///
197 /// You should call this after you're done displaying the Surface.
198 ///
199 virtual void ClearDirtyBounds();
200
201 ///
202 /// The most dirty rectangles the library keeps apart before merging them.
203 ///
204 static constexpr uint32_t kMaxDirtyRects = 8;
205
206 ///
207 /// Get the number of dirty rectangles (0 when nothing has been painted since the last clear).
208 ///
209 uint32_t dirty_rect_count() const;
210
211 ///
212 /// Get a dirty rectangle by index.
213 ///
214 /// Each rectangle lies inside dirty_bounds() and the surface. dirty_bounds() is the union of
215 /// these; copy each one instead of the union to move fewer pixels when the page changed in
216 /// several places (eg, a scrolled page with a fixed header). If you only ever copy
217 /// dirty_bounds(), you can ignore this list.
218 ///
219 /// @param index A value below dirty_rect_count().
220 ///
221 /// @return Returns the rectangle, or an empty rectangle for an index out of range.
222 ///
223 IntRect dirty_rect(uint32_t index) const;
224
225 protected:
227
230 uint32_t dirty_rect_count_ = 0;
231};
232
233///
234/// User-defined factory to provide your own surface implementation.
235///
236/// The library uses this to create/destroy Surface instances when rendering Views on the CPU.
237///
238/// @pre This is automatically managed for you when using App::Create(), if you want to override
239/// Surface or SurfaceFactory, you'll need to use Renderer::Create() instead.
240///
241/// ## Setting the Surface Factory
242///
243/// The default factory creates/destroys a BitmapSurface but you can override this by providing your
244/// own factory to Platform::set_surface_factory().
245///
247 public:
249
250 ///
251 /// Create a native Surface with a certain width and height (in pixels).
252 ///
253 virtual Surface* CreateSurface(uint32_t width, uint32_t height) = 0;
254
255 ///
256 /// Destroy a native Surface previously created by CreateSurface().
257 ///
258 virtual void DestroySurface(Surface* surface) = 0;
259};
260
261///
262/// The default surface implementation, backed by a bitmap.
263///
264/// This is automatically provided by the library when you call Renderer::Create() without defining
265/// a custom SurfaceFactory.
266///
267/// This implementation uses a Bitmap to store pixel data (retrieve it via BitmapSurface::bitmap()).
268///
270 public:
271 virtual uint32_t width() const override;
272
273 virtual uint32_t height() const override;
274
275 virtual uint32_t row_bytes() const override;
276
277 virtual size_t size() const override;
278
279 virtual void* LockPixels() override;
280
281 virtual void UnlockPixels() override;
282
283 virtual void Resize(uint32_t width, uint32_t height) override;
284
285 virtual bool Scroll(const IntRect& rect, int dx, int dy) override;
286
287 ///
288 /// Get the underlying Bitmap.
289 ///
291
292 protected:
293 BitmapSurface(uint32_t width, uint32_t height);
294 virtual ~BitmapSurface();
295 BitmapSurface(const BitmapSurface&) = delete;
296 void operator=(const BitmapSurface&) = delete;
298
299 void* impl_;
300};
301
302///
303/// Get the default Bitmap Surface Factory singleton. (Do not destroy this, this singleton is owned
304/// by the library).
305///
307
308} // namespace ultralight
#define UExport
Definition Exports.h:22
virtual void * LockPixels() override
Lock the pixel buffer and get a pointer to the beginning of the data for reading/writing.
virtual bool Scroll(const IntRect &rect, int dx, int dy) override
Shift a rectangle of pixels by an offset.
virtual uint32_t width() const override
Width (in pixels).
virtual uint32_t row_bytes() const override
Number of bytes between rows (usually width * 4).
BitmapSurface(uint32_t width, uint32_t height)
friend class BitmapSurfaceFactory
Definition Surface.h:297
virtual void Resize(uint32_t width, uint32_t height) override
Resize the pixel buffer to a certain width and height (both in pixels).
virtual size_t size() const override
Size in bytes.
virtual uint32_t height() const override
Height (in pixels).
BitmapSurface(const BitmapSurface &)=delete
RefPtr< Bitmap > bitmap()
Get the underlying Bitmap.
void * impl_
Definition Surface.h:299
virtual void UnlockPixels() override
Unlock the pixel buffer.
void operator=(const BitmapSurface &)=delete
Forward declaration for the LockedPixels class.
Definition Bitmap.h:544
A nullable smart pointer.
Definition RefPtr.h:126
User-defined factory to provide your own surface implementation.
Definition Surface.h:246
virtual Surface * CreateSurface(uint32_t width, uint32_t height)=0
Create a native Surface with a certain width and height (in pixels).
virtual void DestroySurface(Surface *surface)=0
Destroy a native Surface previously created by CreateSurface().
User-defined pixel buffer surface.
Definition Surface.h:44
virtual void ClearDirtyBounds()
Clear the dirty bounds.
IntRect dirty_rect(uint32_t index) const
Get a dirty rectangle by index.
virtual uint32_t height() const =0
Height (in pixels).
LockedPixels< Surface * > LockPixelsSafe()
Lock the pixel buffer for reading/writing (safe version, automatically unlocks).
Definition Surface.h:90
static constexpr uint32_t kMaxDirtyRects
The most dirty rectangles the library keeps apart before merging them.
Definition Surface.h:204
virtual void Resize(uint32_t width, uint32_t height)=0
Resize the pixel buffer to a certain width and height (both in pixels).
virtual uint32_t row_bytes() const =0
Number of bytes between rows (usually width * 4).
IntRect dirty_rects_[kMaxDirtyRects]
Definition Surface.h:229
virtual uint32_t width() const =0
Width (in pixels).
virtual void * LockPixels()=0
Lock the pixel buffer and get a pointer to the beginning of the data for reading/writing.
virtual void UnlockPixels()=0
Unlock the pixel buffer.
static void ShiftPixels(void *pixels, uint32_t row_bytes, const IntRect &rect, int dx, int dy)
Shift a rectangle of 32-bit pixels in place.
IntRect dirty_bounds_
Definition Surface.h:228
virtual size_t size() const =0
Size in bytes.
virtual bool Scroll(const IntRect &rect, int dx, int dy)=0
Shift a rectangle of pixels by an offset.
virtual void set_dirty_bounds(const IntRect &bounds)
Add a rectangle to the dirty area.
virtual IntRect dirty_bounds() const
Get the dirty bounds.
uint32_t dirty_rect_count_
Definition Surface.h:230
uint32_t dirty_rect_count() const
Get the number of dirty rectangles (0 when nothing has been painted since the last clear).
Root namespace for every public Ultralight type, function, and enumeration.
SurfaceFactory * GetBitmapSurfaceFactory()
Get the default Bitmap Surface Factory singleton.
Integer Rectangle Helper.
Definition Geometry.h:533