mirror of
https://github.com/penpot/penpot.git
synced 2026-02-18 15:19:00 -05:00
Compare commits
2 Commits
superalex-
...
staging-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c1ddd3d7d | ||
|
|
30106f8524 |
@@ -1150,3 +1150,24 @@
|
||||
[changes]
|
||||
(::page-id (meta changes)))
|
||||
|
||||
|
||||
(defn set-text-content
|
||||
[changes id content prev-content]
|
||||
(assert-page-id! changes)
|
||||
(let [page-id (::page-id (meta changes))
|
||||
|
||||
redo-change
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id id
|
||||
:operations [{:type :set :attr :content :val content}]}
|
||||
|
||||
undo-change
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id id
|
||||
:operations [{:type :set :attr :content :val prev-content}]}]
|
||||
|
||||
(-> changes
|
||||
(update :redo-changes conj redo-change)
|
||||
(update :undo-changes conj undo-change))))
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[app.common.attrs :as attrs]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
@@ -19,6 +20,7 @@
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
@@ -916,11 +918,11 @@
|
||||
(update-in state [:workspace-text-modifier shape-id] {:position-data position-data}))))
|
||||
|
||||
(defn v2-update-text-shape-content
|
||||
[id content & {:keys [update-name? name finalize? save-undo?]
|
||||
:or {update-name? false name nil finalize? false save-undo? true}}]
|
||||
[id content & {:keys [update-name? name finalize? save-undo? original-content]
|
||||
:or {update-name? false name nil finalize? false save-undo? true original-content nil}}]
|
||||
(ptk/reify ::v2-update-text-shape-content
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(watch [it state _]
|
||||
(if (features/active-feature? state "render-wasm/v1")
|
||||
(let [objects (dsh/lookup-page-objects state)
|
||||
shape (get objects id)
|
||||
@@ -970,7 +972,13 @@
|
||||
{:save-undo? false}))
|
||||
(dws/deselect-shape id)
|
||||
(dwsh/delete-shapes #{id})))
|
||||
(rx/of (dwt/finish-transform))))))
|
||||
(rx/of
|
||||
;; This commit is necesary for undo and component propagation
|
||||
;; on finalization
|
||||
(dch/commit-changes
|
||||
(-> (pcb/empty-changes it (:current-page-id state))
|
||||
(pcb/set-text-content id content original-content)))
|
||||
(dwt/finish-transform))))))
|
||||
|
||||
(let [objects (dsh/lookup-page-objects state)
|
||||
shape (get objects id)
|
||||
|
||||
@@ -118,7 +118,8 @@
|
||||
:update-name? update-name?
|
||||
:name generated-name
|
||||
:finalize? true
|
||||
:save-undo? false))))
|
||||
:save-undo? false
|
||||
:original-content original-content))))
|
||||
|
||||
(let [container-node (mf/ref-val container-ref)]
|
||||
(dom/set-style! container-node "opacity" 0)))
|
||||
|
||||
@@ -1566,7 +1566,6 @@ impl RenderState {
|
||||
};
|
||||
|
||||
let mut bounds = drop_filter.compute_fast_bounds(shape_bounds);
|
||||
|
||||
// Account for the shadow offset so the temporary surface fully contains the shifted blur.
|
||||
bounds.offset(world_offset);
|
||||
// Early cull if the shadow bounds are outside the render area.
|
||||
@@ -1574,76 +1573,6 @@ impl RenderState {
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case: blur=0 and spread>0 with zoom>100% - render directly to avoid resampling
|
||||
// artifacts from render_into_filter_surface when spread is large.
|
||||
if scale > 1.0
|
||||
// && combined_blur.is_none()
|
||||
&& shadow.blur <= 0.0
|
||||
// && shadow.spread <= 0.0
|
||||
{
|
||||
// Create filter without dilate first (just offset)
|
||||
let mut shadow_no_spread = transformed_shadow.clone();
|
||||
shadow_no_spread.to_mut().spread = 0.0;
|
||||
let Some(offset_filter) = shadow_no_spread.get_drop_shadow_filter() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// First, render with offset only
|
||||
{
|
||||
let mut shadow_paint = skia::Paint::default();
|
||||
shadow_paint.set_image_filter(offset_filter);
|
||||
shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&shadow_paint);
|
||||
{
|
||||
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
|
||||
drop_canvas.save_layer(&layer_rec);
|
||||
drop_canvas.scale((scale, scale));
|
||||
drop_canvas.translate(translation);
|
||||
}
|
||||
|
||||
self.with_nested_blurs_suppressed(|state| {
|
||||
state.render_shape(
|
||||
&plain_shape,
|
||||
clip_bounds,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::DropShadows,
|
||||
false,
|
||||
Some(shadow.offset),
|
||||
None,
|
||||
);
|
||||
});
|
||||
|
||||
{
|
||||
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
|
||||
drop_canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
// Then apply dilate filter to expand (in scaled space, so multiply by scale)
|
||||
let spread_scaled = shadow.spread * scale;
|
||||
let dilate_filter = skia::image_filters::dilate(
|
||||
(spread_scaled, spread_scaled),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let mut dilate_paint = skia::Paint::default();
|
||||
dilate_paint.set_image_filter(dilate_filter);
|
||||
dilate_paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&dilate_paint);
|
||||
{
|
||||
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
|
||||
drop_canvas.save_layer(&layer_rec);
|
||||
// No additional transformations needed - dilate applies to existing content
|
||||
drop_canvas.restore();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if use_low_zoom_path {
|
||||
let mut shadow_paint = skia::Paint::default();
|
||||
shadow_paint.set_image_filter(drop_filter);
|
||||
@@ -1674,24 +1603,12 @@ impl RenderState {
|
||||
}
|
||||
|
||||
let filter_result =
|
||||
filters::render_into_filter_surface(self, bounds, |state, temp_surface, filter_scale| {
|
||||
filters::render_into_filter_surface(self, bounds, |state, temp_surface| {
|
||||
{
|
||||
let canvas = state.surfaces.canvas(temp_surface);
|
||||
|
||||
// Adjust the filter to compensate for the scaling that render_into_filter_surface
|
||||
// applies. If filter_scale < 1.0, the canvas is scaled down, so we need to
|
||||
// scale up the spread to maintain the correct visual size.
|
||||
let adjusted_filter = if filter_scale < 1.0 && shadow.spread > 0.0 {
|
||||
// Create a new filter with adjusted spread
|
||||
let mut adjusted_shadow = transformed_shadow.clone();
|
||||
adjusted_shadow.to_mut().spread = shadow.spread / filter_scale;
|
||||
adjusted_shadow.get_drop_shadow_filter().unwrap_or(drop_filter.clone())
|
||||
} else {
|
||||
drop_filter.clone()
|
||||
};
|
||||
|
||||
let mut shadow_paint = skia::Paint::default();
|
||||
shadow_paint.set_image_filter(adjusted_filter);
|
||||
shadow_paint.set_image_filter(drop_filter.clone());
|
||||
shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&shadow_paint);
|
||||
|
||||
@@ -40,9 +40,7 @@ pub fn render_with_filter_surface<F>(
|
||||
where
|
||||
F: FnOnce(&mut RenderState, SurfaceId),
|
||||
{
|
||||
if let Some((mut surface, scale)) = render_into_filter_surface(render_state, bounds, |state, surface_id, _filter_scale| {
|
||||
draw_fn(state, surface_id)
|
||||
}) {
|
||||
if let Some((mut surface, scale)) = render_into_filter_surface(render_state, bounds, draw_fn) {
|
||||
let canvas = render_state.surfaces.canvas_and_mark_dirty(target_surface);
|
||||
|
||||
// If we scaled down, we need to scale the source rect and adjust the destination
|
||||
@@ -71,15 +69,13 @@ where
|
||||
/// down so that everything fits; the returned `scale` tells the caller how much the
|
||||
/// content was reduced so it can be re-scaled on compositing. The `draw_fn` should
|
||||
/// render the untransformed shape (i.e. in document coordinates) onto `SurfaceId::Filter`.
|
||||
/// The `draw_fn` receives the `filter_scale` that will be applied, allowing it to
|
||||
/// adjust filters (like spread) accordingly.
|
||||
pub fn render_into_filter_surface<F>(
|
||||
render_state: &mut RenderState,
|
||||
bounds: Rect,
|
||||
draw_fn: F,
|
||||
) -> Option<(skia::Surface, f32)>
|
||||
where
|
||||
F: FnOnce(&mut RenderState, SurfaceId, f32),
|
||||
F: FnOnce(&mut RenderState, SurfaceId),
|
||||
{
|
||||
if !bounds.is_finite() || bounds.width() <= 0.0 || bounds.height() <= 0.0 {
|
||||
return None;
|
||||
@@ -109,7 +105,7 @@ where
|
||||
canvas.translate((-bounds.left, -bounds.top));
|
||||
}
|
||||
|
||||
draw_fn(render_state, filter_id, scale);
|
||||
draw_fn(render_state, filter_id);
|
||||
|
||||
render_state.surfaces.canvas(filter_id).restore();
|
||||
|
||||
|
||||
@@ -215,22 +215,6 @@ impl Surfaces {
|
||||
);
|
||||
}
|
||||
|
||||
/// Draws one surface into another using nearest-neighbor sampling to avoid interpolation blur.
|
||||
/// Use this for DropShadows when rendering sharp shadows (blur=0, spread=0) at high zoom.
|
||||
pub fn draw_into_nearest(&mut self, from: SurfaceId, to: SurfaceId, paint: Option<&skia::Paint>) {
|
||||
let nearest_sampling = skia::SamplingOptions::new(
|
||||
skia::FilterMode::Nearest,
|
||||
skia::MipmapMode::None,
|
||||
);
|
||||
|
||||
self.get_mut(from).clone().draw(
|
||||
self.canvas_and_mark_dirty(to),
|
||||
(0.0, 0.0),
|
||||
nearest_sampling,
|
||||
paint,
|
||||
);
|
||||
}
|
||||
|
||||
/// Draws the cache surface directly to the target canvas.
|
||||
/// This avoids creating an intermediate snapshot, reducing GPU stalls.
|
||||
pub fn draw_cache_to_target(&mut self) {
|
||||
|
||||
Reference in New Issue
Block a user