diff --git a/README.md b/README.md index fa107a2..8a2f239 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ No, I'm building a tool that does the bare minimum what I require and what I don find in other open source tools. That doesn't mean of course that contributions related to Photoshop-esque features aren't welcomed. -### All self-written ? +### All hand-written ? Yep, though it helps having worked in the photo software industry for five years, having tackled the problems before. Also, Bitmappery is reusing [zCanvas](https://github.com/igorski/zCanvas) @@ -25,6 +25,23 @@ BitMappery does however make use of the following excellent libraries to speed u * UI Essentials by [www.wishforge.games](https://freeicons.io/profile/2257) * Black arrow icons set by [Reda](https://freeicons.io/profile/6156) +## Model + +BitMappery works with entities known as _Documents_. A Document contains several _Layer_s, each of +which define their content, transformation, _Effect_s, etc. Each of the nested entity properties +has its own factory (see _@/src/factories/_). The Document is managed by the Vuex _document-module.js_. + +## Document rendering and interactions + +The Document is rendered one layer at a time onto a Canvas element, using [zCanvas](https://github.com/igorski/zCanvas). Both the rendering and interaction handling is performed by _@/src/components/ui/zcanvas/layer_sprite.js_. +Note that the purpose of the renderer is solely to delegate interactions events to the Layer entity. The +renderer should represent the properties of the Layer, the Layer should never reverse-engineer from the onscreen +content (especially as different window size and scaling factor will greatly complicate these matters when +performed two-way). + +Rendering transformations, text and effects is an asynchronous operation handled by _@/src/services/render-service.js_. The purpose of this service is to perform and cache repeated operations and eventually maintain +the source bitmap represented by the _layer-sprite.js_. + ## Dropbox integration Requires you to [register a client id or access token](https://www.dropbox.com/developers/apps). @@ -56,7 +73,10 @@ npm run lint # TODO / Roadmap +* Implement iframe based rendering (as more compatible alternative to OffscreenCanvas) for effects +* Implement action queue when drawing, only execute drawing on update() hook * Implement loaders on document load/save, image export and dropbox import +* Maintain cache for transformations and filters, rendered at the display destination size (invalidate on window resize) * Drawing masks on a rotated layer that is panned (or mirrored) is broken * Dragging of masks on rotated/mirror content is kinda broken * Restoring of document with rotated layers (smaller than document size) restores at incorrect offset diff --git a/src/components/application-menu/application-menu.vue b/src/components/application-menu/application-menu.vue index 5e33245..860d9c9 100644 --- a/src/components/application-menu/application-menu.vue +++ b/src/components/application-menu/application-menu.vue @@ -65,7 +65,7 @@
  • @@ -240,7 +240,7 @@ export default { requestDocumentResize() { this.openModal( RESIZE_DOCUMENT ); }, - requestDocumentSave() { + requestDocumentExport() { this.openModal( EXPORT_DOCUMENT ); }, requestSelectionLoad() { diff --git a/src/services/keyboard-service.js b/src/services/keyboard-service.js index f2d55aa..ba6e8dc 100644 --- a/src/services/keyboard-service.js +++ b/src/services/keyboard-service.js @@ -20,10 +20,11 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { ADD_LAYER } from "@/definitions/modal-windows"; import { LAYER_TEXT } from "@/definitions/layer-types"; import ToolTypes, { MAX_BRUSH_SIZE, MIN_ZOOM, MAX_ZOOM, canDraw } from "@/definitions/tool-types"; -import { DROPBOX_FILE_SELECTOR, SAVE_DROPBOX_DOCUMENT } from "@/definitions/modal-windows"; +import { + ADD_LAYER, EXPORT_DOCUMENT, DROPBOX_FILE_SELECTOR, SAVE_DROPBOX_DOCUMENT +} from "@/definitions/modal-windows"; import { getCanvasInstance, getSpriteForLayer } from "@/factories/sprite-factory"; let state, getters, commit, dispatch, listener, @@ -208,7 +209,9 @@ function handleKeyDown( event ) { break; case 69: // E - if ( canDraw( getters.activeDocument, getters.activeLayer )) { + if ( hasOption ) { + openModal( EXPORT_DOCUMENT ); + } else if ( canDraw( getters.activeDocument, getters.activeLayer )) { setActiveTool( ToolTypes.ERASER ); } break; @@ -220,14 +223,16 @@ function handleKeyDown( event ) { break; case 73: // I - if ( getters.activeLayer ) { + if ( hasOption ) { + dispatch( "loadDocument" ); + } else if ( getters.activeLayer ) { setActiveTool( ToolTypes.EYEDROPPER ); } break; case 76: // L if ( hasOption && shiftDown ) { - commit( "openModal", ADD_LAYER ); + openModal( ADD_LAYER ); } else { commit( "setActiveTool", { tool: ToolTypes.LASSO }) setActiveTool( ToolTypes.LASSO ); @@ -251,7 +256,7 @@ function handleKeyDown( event ) { case 79: // O if ( hasOption ) { if ( state.dropboxConnected ) { - commit( "openModal", DROPBOX_FILE_SELECTOR ); + openModal( DROPBOX_FILE_SELECTOR ); } preventDefault( event ); } @@ -264,7 +269,7 @@ function handleKeyDown( event ) { case 83: // S if ( hasOption ) { if ( getters.activeDocument && state.dropboxConnected ) { - commit( "openModal", SAVE_DROPBOX_DOCUMENT ); + openModal( SAVE_DROPBOX_DOCUMENT ); } preventDefault( event ); // page save } @@ -388,3 +393,7 @@ function preventDefault( event ) { function setActiveTool( tool ) { commit( "setActiveTool", { tool, activeLayer: getters.activeLayer }); } + +function openModal( modal ) { + commit( "openModal", modal ); +}