peter-kuhmann / pan-n-zoom

Animate panning and zooming within your images! 🐈 Free, features offline mode, desktop app and help videos. ⭐️
https://pan-n-zoom.peter-kuhmann.de
MIT License
8 stars 0 forks source link

Support for layers #1

Open jshph opened 10 months ago

jshph commented 10 months ago

Hi Peter, great project -- I happened to stumble across this on YouTube and it's exactly the kind of lightweight solution I'm looking for.

I'm curious if you have considered support for adding layers of images? My use case would be to gradually add and remove certain components from the Excalidraw diagram over the course of a presentation. My opinion is that the easiest way to do this as a user would be to:

Let me know if that brief description makes sense; happy to elaborate or perhaps contribute on any fronts that make sense for you.

peter-kuhmann commented 10 months ago

Hi Joshua, Thanks for your kind words! I apologize for answering this late – I was on a 4-week long vacation and had not a suitable device to write this long answer. 😅


Regarding your question: Yes, I considered such a feature. Let me give you a short insight into how it started:

As you can see, I stuck to that concept. Panning and zooming are necessary; "revealing" elements can be done by increasing the next keyframe's size – that was the idea. But it's suboptimal.

Having both features (step-by-step panning/zooming and step-by-step masks) would allow to:

My initial "MVP" list contained the feature "allow the user to blur non-keyframe area" – but I ditched it. I think it would be awesome to reveal elements/areas step-by-step!


While hiking in Slovenia, I had time to think about the problem. I see two options from the architecture perspective:

Intuitively, I would pick (b) because of the following aspects:

When I had this thought of a "mask editor", immediately the Adobe Lightroom mask drawing mode (red mask -> https://helpx.adobe.com/content/dam/help/en/lightroom-classic/help/whats-new/2022/select_subject_masking-new.png.img.png) popped up in my head. 😅


I am super interested in your thoughts! I am also happy to brainstorm together! 🙂

Peter

jshph commented 10 months ago

Agreed on all fronts, and thanks for the additional context on the MVP. Hope you had a good time off :)

The SVG mask idea makes sense based on how I would use it -- perhaps an optional, editable mask that can be applied or copied to each keyframe. A simple version of that could allow for drawing a rectangle over the image, and reduce the opacity / gray-out the areas outside of the rectangle. The rectangle could be stored as SVG or simply as a bounding box, rendered for each keyframe.

Similar to Lightroom would be the layer masks in Adobe Illustrator/Photoshop, where the shapes used for the mask can be vector-based.

peter-kuhmann commented 10 months ago

It might make sense to restructure the app/editor to have a more complex but still simple timeline view – similar to a video editor.

The image below shows a brain dump:

What do you think?

image

jshph commented 10 months ago

Yeah that all sounds good to me. It lines up with my mental model of how Adobe's masks work -- initialized to the full view, with the difference being that if there is a prior keyframe then that keyframe's mask gets inherited.

peter-kuhmann commented 9 months ago

Hey @jshph!

This week, I will experiment with run-length encoded mask "bitmaps." It's probably way simpler to encode a "mask canvas" to a 0/1 run-length encoded bitmap. I will probably use that approach if it doesn't consume too much memory space.

jshph commented 9 months ago

That would be a clever bit of engineering. Thanks Peter! Eager to hear where it goes.

peter-kuhmann commented 9 months ago

I create some simple tests regarding RLE encoded bitmaps.

Assuming

The format is "{width}x{height}:T{runLength}M{runLength}T{runLength}M...".


I tried to (mis)use UTF-8 to store run lengths using fewer bytes (via String.fromCharCode(runLength)) to compress the RLE-encoded bitmap even more.

Format is "{String.fromCharCode(width)}{String.fromCharCode(height)}{firstRunMasked ? 'M' : 'T'}{String.fromCharCode(runLength)}{String.fromCharCode(runLength)}..."

Results: UTF-8-hacky-RLE-encoded bitmaps are 3x smaller on average. 🤩 So around 1,5 - 5KB for the bitmap resolution state above. That is very acceptable.


To me, this looks like the best option:

Peter

jshph commented 8 months ago

Hi Peter, sorry I've had a pretty busy weekend / week!

That's incredibly promising, thank you for taking the time to share about it!! I'm not sure I see any downsides from the UTF-8 optimization; potentially, char codes are less interpretable, but not sure that is important to optimize. Certainly, a lightweight experience would be much appreciated :)

Let me know how I could help here -- any PRs to review?

Josh