mirror of
https://github.com/penpot/penpot.git
synced 2026-08-07 05:18:36 +00:00
🐛 Fix client-side wasm export encoding jpeg and webp as png
This commit is contained in:
parent
bcc8399bcc
commit
0dbae06c45
@ -13,7 +13,10 @@
|
||||
|
||||
(defn export-image-uri
|
||||
[{:keys [type scale object-id]}]
|
||||
(let [bytes (wasm.api/render-shape-pixels object-id scale)
|
||||
;; The export type doubles as the encoder format, so the bytes always match
|
||||
;; the mtype we wrap them in. `:svg` never reaches here — it needs vector
|
||||
;; markup and `exports.assets/wasm-export-types` keeps it off this path.
|
||||
(let [bytes (wasm.api/render-shape-pixels object-id scale type)
|
||||
mtype (format->mtype type)
|
||||
blob (wapi/create-blob bytes mtype)]
|
||||
(wapi/create-uri blob)))
|
||||
|
||||
@ -68,7 +68,9 @@
|
||||
{sel-w :width sel-h :height} (:selrect frame)
|
||||
max-size (mth/max (or ext-w sel-w) (or ext-h sel-h))
|
||||
scale (mth/max 1 (/ target-size max-size))
|
||||
png-bytes (wasm.api/render-shape-pixels frame-id scale)]
|
||||
;; Thumbnails are always PNG: they need the alpha
|
||||
;; channel and are consumed as a `image/png` data uri.
|
||||
png-bytes (wasm.api/render-shape-pixels frame-id scale :png)]
|
||||
(if (or (nil? png-bytes) (zero? (.-length png-bytes)))
|
||||
(do
|
||||
(l/error :hint "render-shape-pixels returned empty" :frame-id (str frame-id))
|
||||
|
||||
@ -2629,7 +2629,10 @@
|
||||
(reset! transition-image* snapshot)))))
|
||||
|
||||
(defn render-shape-pixels
|
||||
[shape-id scale]
|
||||
"Renders a shape subtree to encoded image bytes. `format` is :png, :jpeg or
|
||||
:webp; jpeg is flattened onto white on the Rust side, since it has no alpha
|
||||
channel."
|
||||
[shape-id scale format]
|
||||
(let [buffer (uuid/get-u32 shape-id)
|
||||
|
||||
offset
|
||||
@ -2638,7 +2641,8 @@
|
||||
(aget buffer 1)
|
||||
(aget buffer 2)
|
||||
(aget buffer 3)
|
||||
scale)
|
||||
scale
|
||||
(sr/translate-raster-format format))
|
||||
|
||||
heap (mem/get-heap-u8)
|
||||
heapu32 (mem/get-heap-u32)
|
||||
|
||||
@ -976,6 +976,9 @@ pub extern "C" fn get_shape_extrect(a: u32, b: u32, c: u32, d: u32) -> Result<*m
|
||||
})
|
||||
}
|
||||
|
||||
/// Raster image via the GPU surface. Returns `[len][width][height][bytes]`
|
||||
/// (LE). `format` selects the encoder: 0 = PNG, 1 = JPEG, 2 = WEBP (see
|
||||
/// `RasterFormat`).
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn render_shape_pixels(
|
||||
@ -984,6 +987,7 @@ pub extern "C" fn render_shape_pixels(
|
||||
c: u32,
|
||||
d: u32,
|
||||
scale: f32,
|
||||
format: u32,
|
||||
) -> Result<*mut u8> {
|
||||
let id = uuid_from_u32_quartet(a, b, c, d);
|
||||
|
||||
@ -991,9 +995,11 @@ pub extern "C" fn render_shape_pixels(
|
||||
return Err(Error::CriticalError("Scale is not finite".to_string()));
|
||||
}
|
||||
|
||||
let format = RasterFormat::from_u32(format)?;
|
||||
|
||||
with_state!(state, {
|
||||
let (data, width, height) =
|
||||
state.render_shape_pixels(&id, scale, performance::get_time())?;
|
||||
state.render_shape_pixels(&id, scale, performance::get_time(), format)?;
|
||||
|
||||
let len = data.len() as u32;
|
||||
let mut buf = Vec::with_capacity(4 + data.len());
|
||||
|
||||
@ -2429,6 +2429,7 @@ impl RenderState {
|
||||
tree: ShapesPoolRef,
|
||||
scale: f32,
|
||||
timestamp: i32,
|
||||
format: raster::RasterFormat,
|
||||
) -> Result<(Vec<u8>, i32, i32)> {
|
||||
let target_surface = SurfaceId::Export;
|
||||
|
||||
@ -2459,7 +2460,7 @@ impl RenderState {
|
||||
|
||||
self.surfaces
|
||||
.canvas(target_surface)
|
||||
.clear(skia::Color::TRANSPARENT);
|
||||
.clear(format.clear_color());
|
||||
|
||||
if tree.len() != 0 {
|
||||
let Some(shape) = tree.get(id) else {
|
||||
@ -2476,6 +2477,14 @@ impl RenderState {
|
||||
self.render_area_with_margins = extrect;
|
||||
self.surfaces.update_render_context(extrect, scale);
|
||||
|
||||
// `resize_export_surface` swaps in a brand-new (zeroed, i.e.
|
||||
// transparent) surface whenever the dimensions change, discarding
|
||||
// the clear above — so an opaque backdrop has to be laid down again
|
||||
// here, after the resize.
|
||||
if let Some(background) = format.opaque_background() {
|
||||
self.surfaces.canvas(target_surface).clear(background);
|
||||
}
|
||||
|
||||
self.pending_nodes.push(NodeRenderState {
|
||||
id: *id,
|
||||
visited_children: false,
|
||||
@ -2496,10 +2505,10 @@ impl RenderState {
|
||||
let data = image
|
||||
.encode(
|
||||
Some(&mut get_gpu_state().context),
|
||||
skia::EncodedImageFormat::PNG,
|
||||
100,
|
||||
format.encoded(),
|
||||
format.quality(),
|
||||
)
|
||||
.expect("PNG encode failed");
|
||||
.unwrap_or_else(|| panic!("{format:?} encode failed"));
|
||||
let skia::ISize { width, height } = image.dimensions();
|
||||
|
||||
// Restore the workspace render state.
|
||||
|
||||
@ -96,8 +96,9 @@ impl State {
|
||||
id: &Uuid,
|
||||
scale: f32,
|
||||
timestamp: i32,
|
||||
format: RasterFormat,
|
||||
) -> Result<(Vec<u8>, i32, i32)> {
|
||||
get_render_state().render_shape_pixels(id, &self.shapes, scale, timestamp)
|
||||
get_render_state().render_shape_pixels(id, &self.shapes, scale, timestamp, format)
|
||||
}
|
||||
|
||||
pub fn render_shape_pdf(&mut self, id: &Uuid, scale: f32) -> Result<Vec<u8>> {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user