diff --git a/src/css/toolbox-palettes-list.css b/src/css/toolbox-palettes-list.css index 25f5d8053..e3196ae94 100644 --- a/src/css/toolbox-palettes-list.css +++ b/src/css/toolbox-palettes-list.css @@ -32,9 +32,8 @@ } .palettes-list-select { - width: 66.66667%; + width: 66%; height: 100%; - padding: 0 5px 0 5px; color: #aaa; font-size : 0.75em; diff --git a/src/js/Constants.js b/src/js/Constants.js index 189554173..6bb825664 100644 --- a/src/js/Constants.js +++ b/src/js/Constants.js @@ -28,6 +28,8 @@ var Constants = { CURRENT_COLORS_PALETTE_ID : '__current-colors', + CURRENT_FRAME_COLORS_PALETTE_ID : '__current-frame-colors', + /* * Fake semi-transparent color used to highlight transparent * strokes and rectangles: diff --git a/src/js/Events.js b/src/js/Events.js index 9421849c7..5d8887cf8 100644 --- a/src/js/Events.js +++ b/src/js/Events.js @@ -81,6 +81,12 @@ var Events = { CURRENT_COLORS_UPDATED : 'CURRENT_COLORS_UPDATED', + CURRENT_FRAME_CHANGED: 'CURRENT_FRAME_CHANGED', + + CURRENT_LAYER_CHANGED: 'CURRENT_LAYER_CHANGED', + + CURRENT_PALETTE_CHANGED: 'CURRENT_PALETTE_CHANGED', + PERFORMANCE_REPORT_CHANGED : 'PERFORMANCE_REPORT_CHANGED', PISKEL_FILE_IMPORT_FAILED : 'PISKEL_FILE_IMPORT_FAILED', diff --git a/src/js/app.js b/src/js/app.js index 6b43e5814..f14a1740d 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -43,6 +43,7 @@ this.paletteService = new pskl.service.palette.PaletteService(); this.paletteService.addDynamicPalette(new pskl.service.palette.CurrentColorsPalette()); + this.paletteService.addDynamicPalette(new pskl.service.palette.CurrentFrameColorsPalette()); this.selectedColorsService = new pskl.service.SelectedColorsService(); this.selectedColorsService.init(); diff --git a/src/js/controller/PalettesListController.js b/src/js/controller/PalettesListController.js index fcc7f072c..0936fa469 100644 --- a/src/js/controller/PalettesListController.js +++ b/src/js/controller/PalettesListController.js @@ -77,6 +77,7 @@ ns.PalettesListController.prototype.selectPalette = function (paletteId) { pskl.UserSettings.set(pskl.UserSettings.SELECTED_PALETTE, paletteId); + $.publish(Events.CURRENT_PALETTE_CHANGED) }; ns.PalettesListController.prototype.getSelectedPaletteColors_ = function () { @@ -148,8 +149,10 @@ }; ns.PalettesListController.prototype.onCreatePaletteClick_ = function (evt) { + var paletteId = this.colorPaletteSelect_.value; $.publish(Events.DIALOG_SHOW, { - dialogId : 'create-palette' + dialogId : 'create-palette', + initArgs : paletteId }); }; diff --git a/src/js/controller/TransformationsController.js b/src/js/controller/TransformationsController.js index 0ccb427b9..501a2540c 100644 --- a/src/js/controller/TransformationsController.js +++ b/src/js/controller/TransformationsController.js @@ -9,6 +9,7 @@ new pskl.tools.transform.Clone(), new pskl.tools.transform.Center(), new pskl.tools.transform.Crop(), + new pskl.tools.transform.PaletteApply(), ]; this.toolIconBuilder = new pskl.tools.ToolIconBuilder(); diff --git a/src/js/controller/dialogs/CreatePaletteController.js b/src/js/controller/dialogs/CreatePaletteController.js index 4d9827e19..e9e354303 100644 --- a/src/js/controller/dialogs/CreatePaletteController.js +++ b/src/js/controller/dialogs/CreatePaletteController.js @@ -30,7 +30,7 @@ this.colorsListWidget = new pskl.widgets.ColorsList(colorsListContainer); var palette; - var isCurrentColorsPalette = paletteId == Constants.CURRENT_COLORS_PALETTE_ID; + var isCurrentColorsPalette = paletteId == Constants.CURRENT_COLORS_PALETTE_ID || paletteId == Constants.CURRENT_FRAME_COLORS_PALETTE_ID; if (paletteId && !isCurrentColorsPalette) { importFileButton.style.display = 'none'; this.setTitle('Edit Palette'); @@ -43,8 +43,11 @@ this.setTitle('Create Palette'); var uuid = pskl.utils.Uuid.generate(); - if (isCurrentColorsPalette) { - palette = new pskl.model.Palette(uuid, 'Current colors clone', this.getCurrentColors_()); + if (paletteId == Constants.CURRENT_COLORS_PALETTE_ID) { + palette = new pskl.model.Palette(uuid, 'File colors copy', this.getCurrentColors_()); + } else if (paletteId == Constants.CURRENT_FRAME_COLORS_PALETTE_ID) { + var frameId = pskl.app.piskelController.getCurrentFrameIndex() + 1; + palette = new pskl.model.Palette(uuid, 'frame:' + frameId + ' colors copy', this.getCurrentFrameColors_()); } else { palette = new pskl.model.Palette(uuid, 'New palette', []); } @@ -58,6 +61,11 @@ return palette.getColors(); }; + ns.CreatePaletteController.prototype.getCurrentFrameColors_ = function () { + var palette = this.paletteService.getPaletteById(Constants.CURRENT_FRAME_COLORS_PALETTE_ID); + return palette.getColors(); + }; + ns.CreatePaletteController.prototype.setPalette_ = function (palette) { this.palette = palette; this.nameInput.value = pskl.utils.unescapeHtml(palette.name); diff --git a/src/js/controller/piskel/PiskelController.js b/src/js/controller/piskel/PiskelController.js index 3e2fe4198..b3b4f6501 100644 --- a/src/js/controller/piskel/PiskelController.js +++ b/src/js/controller/piskel/PiskelController.js @@ -236,6 +236,7 @@ ns.PiskelController.prototype.setCurrentFrameIndex = function (index) { if (this.hasFrameAt(index)) { this.currentFrameIndex = index; + $.publish(Events.CURRENT_FRAME_CHANGED); } else { window.console.error('Could not set current frame index to ' + index); } @@ -258,6 +259,7 @@ ns.PiskelController.prototype.setCurrentLayerIndex = function (index) { if (this.hasLayerAt(index)) { this.currentLayerIndex = index; + $.publish(Events.CURRENT_LAYER_CHANGED); } else { window.console.error('Could not set current layer index to ' + index); } diff --git a/src/js/model/Frame.js b/src/js/model/Frame.js index 827d68986..d6cc3a7b9 100644 --- a/src/js/model/Frame.js +++ b/src/js/model/Frame.js @@ -8,6 +8,7 @@ this.id = __idCounter++; this.version = 0; this.pixels = ns.Frame.createEmptyPixelGrid_(width, height); + this.colorPalette = []; this.stateIndex = 0; } else { throw 'Bad arguments in pskl.model.Frame constructor : ' + width + ', ' + height; @@ -106,14 +107,16 @@ return [this.id, this.version].join('-'); }; - ns.Frame.prototype.setPixel = function (x, y, color) { + ns.Frame.prototype.setPixel = function (x, y, entryColor) { if (this.containsPixel(x, y)) { var index = y * this.width + x; var p = this.pixels[index]; - color = pskl.utils.colorToInt(color); - + var color = pskl.utils.colorToInt(entryColor); + if (p !== color) { - this.pixels[index] = color || pskl.utils.colorToInt(Constants.TRANSPARENT_COLOR); + var applyColor = color || pskl.utils.colorToInt(Constants.TRANSPARENT_COLOR); + this.pixels[index] = applyColor; + this.version++; } } diff --git a/src/js/service/CurrentColorsService.js b/src/js/service/CurrentColorsService.js index 443e53ae5..cd5c1242b 100644 --- a/src/js/service/CurrentColorsService.js +++ b/src/js/service/CurrentColorsService.js @@ -6,6 +6,7 @@ // cache of current colors by history state this.cache = {}; this.currentColors = []; + this.currentFrameColors = []; this.cachedFrameProcessor = new pskl.model.frame.AsyncCachedFrameProcessor(); this.cachedFrameProcessor.setFrameProcessor(this.getFrameColors_.bind(this)); @@ -21,12 +22,19 @@ ns.CurrentColorsService.prototype.init = function () { $.subscribe(Events.HISTORY_STATE_SAVED, this.throttledUpdateCurrentColors_); $.subscribe(Events.HISTORY_STATE_LOADED, this.loadColorsFromCache_.bind(this)); + $.subscribe(Events.CURRENT_FRAME_CHANGED, this.updateCurrentColors_.bind(this)); + $.subscribe(Events.CURRENT_LAYER_CHANGED, this.updateCurrentColors_.bind(this)); + $.subscribe(Events.CURRENT_PALETTE_CHANGED, this.updateCurrentColors_.bind(this)); }; ns.CurrentColorsService.prototype.getCurrentColors = function () { return this.currentColors; }; + ns.CurrentColorsService.prototype.getCurrentFrameColors = function () { + return this.currentFrameColors + }; + ns.CurrentColorsService.prototype.setCurrentColors = function (colors) { var historyIndex = pskl.app.historyService.currentIndex; this.cache[historyIndex] = colors; @@ -36,6 +44,43 @@ } }; + // Current frame colors are tracked and updated for any future pixel indexing operations + ns.CurrentColorsService.prototype.updateCurrentFrameColors = function (frame) { + var currentFrameIndex = pskl.app.piskelController.getCurrentFrameIndex(); + var frame = pskl.app.piskelController.getCurrentLayer().getFrameAt(currentFrameIndex); + + this.updateFrameColors(frame); + $.publish(Events.CURRENT_COLORS_UPDATED); + }; + + ns.CurrentColorsService.prototype.updateFrameColors = function (frame) { + frame.colorPalette = []; + frame.forEachPixel(function (color, col, row, frame) { + if (color === 0) return; + if (!frame.colorPalette.includes(color)) { + frame.colorPalette.push(color) + } + }) + + // this.currentFrameColors keeps them as hexes, for the palette + this.currentFrameColors = frame.colorPalette.map(function(intColor) { + return pskl.utils.intToHex(intColor) + }) + } + + ns.CurrentColorsService.prototype.applyCurrentPaletteToIndexedPixels = function (applicationPalette, frame, update) { + this.updateFrameColors(frame); + applicationPalette.forEach(function(color, index) { + frame.forEachPixel(function (oldColor, col, row, frame) { + if (oldColor === 0) return; + var newPixelIndex = frame.colorPalette.indexOf(oldColor) + if (newPixelIndex === index) { + frame.setPixel(col, row, color); + } + }) + }); + } + ns.CurrentColorsService.prototype.isCurrentColorsPaletteSelected_ = function () { var paletteId = pskl.UserSettings.get(pskl.UserSettings.SELECTED_PALETTE); var palette = this.paletteService.getPaletteById(paletteId); @@ -97,6 +142,7 @@ return pskl.utils.intToHex(color); }); this.setCurrentColors(hexColors); + this.updateCurrentFrameColors(); }.bind(this)); }; diff --git a/src/js/service/palette/CurrentColorsPalette.js b/src/js/service/palette/CurrentColorsPalette.js index 46fa8d378..4d4595bdb 100644 --- a/src/js/service/palette/CurrentColorsPalette.js +++ b/src/js/service/palette/CurrentColorsPalette.js @@ -2,7 +2,7 @@ var ns = $.namespace('pskl.service.palette'); ns.CurrentColorsPalette = function () { - this.name = 'Current colors'; + this.name = '* Current File *'; this.id = Constants.CURRENT_COLORS_PALETTE_ID; this.colorSorter = new pskl.service.color.ColorSorter(); }; diff --git a/src/js/service/palette/CurrentFrameColorsPalette.js b/src/js/service/palette/CurrentFrameColorsPalette.js new file mode 100644 index 000000000..9d61450f2 --- /dev/null +++ b/src/js/service/palette/CurrentFrameColorsPalette.js @@ -0,0 +1,15 @@ +(function () { + var ns = $.namespace('pskl.service.palette'); + + ns.CurrentFrameColorsPalette = function () { + this.name = '- Current Frame -'; + this.id = Constants.CURRENT_FRAME_COLORS_PALETTE_ID; + this.colorSorter = new pskl.service.color.ColorSorter(); + }; + + ns.CurrentFrameColorsPalette.prototype.getColors = function () { + var currentColors = pskl.app.currentColorsService.getCurrentFrameColors(); + currentColors = currentColors.slice(0, Constants.MAX_PALETTE_COLORS); + return currentColors; + }; +})(); diff --git a/src/js/tools/transform/PaletteApply.js b/src/js/tools/transform/PaletteApply.js new file mode 100644 index 000000000..cde8828b5 --- /dev/null +++ b/src/js/tools/transform/PaletteApply.js @@ -0,0 +1,36 @@ +(function () { + var ns = $.namespace('pskl.tools.transform'); + + ns.PaletteApply = function () { + this.toolId = 'tool-colorswap'; + this.helpText = "Apply the currently selected palette's colors to a frame via their index numbers"; + this.tooltipDescriptors = [ + {key : 'ctrl', description : 'Apply to all layers'}, + {key : 'shift', description : 'Apply to all frames'} + ]; + }; + + pskl.utils.inherit(ns.PaletteApply, ns.AbstractTransformTool); + + ns.PaletteApply.prototype.applyToolOnFrame_ = function (frame, altKey) { + + var allLayers = pskl.utils.UserAgent.isMac ? event.metaKey : event.ctrlKey; + var allFrames = event.shiftKey; + + var currentPalette = pskl.app.palettesListController.getSelectedPaletteColors_(); + this.swapColors_(currentPalette, allLayers, allFrames) + + }; + + ns.PaletteApply.prototype.swapColors_ = function(newPalette, allLayers, allFrames) { + var currentFrameIndex = pskl.app.piskelController.getCurrentFrameIndex(); + var layers = allLayers ? pskl.app.piskelController.getLayers() : [pskl.app.piskelController.getCurrentLayer()]; + layers.forEach(function (layer) { + var frames = allFrames ? layer.getFrames() : [layer.getFrameAt(currentFrameIndex)]; + frames.forEach(function (frame) { + pskl.app.currentColorsService.applyCurrentPaletteToIndexedPixels(newPalette, frame, true); + }.bind(this)); + }.bind(this)); + } + +})(); \ No newline at end of file diff --git a/src/js/utils/PixelUtils.js b/src/js/utils/PixelUtils.js index a992cf1f2..061d2daba 100644 --- a/src/js/utils/PixelUtils.js +++ b/src/js/utils/PixelUtils.js @@ -99,8 +99,7 @@ /** * Apply the paintbucket tool in a frame at the (col, row) initial position * with the replacement color. - * - * @param frame pskl.model.Frame The frame target in which we want to paintbucket + * @param frame pskl.model.Frame The target in which we want to paintbucket. We set color on this frame. * @param col number Column coordinate in the frame * @param row number Row coordinate in the frame * @param replacementColor string Hexadecimal color used to fill the area diff --git a/src/piskel-script-list.js b/src/piskel-script-list.js index 11352a033..1fcb054cb 100644 --- a/src/piskel-script-list.js +++ b/src/piskel-script-list.js @@ -188,6 +188,7 @@ "js/service/HistoryService.js", "js/service/color/ColorSorter.js", "js/service/palette/CurrentColorsPalette.js", + "js/service/palette/CurrentFrameColorsPalette.js", "js/service/palette/PaletteService.js", "js/service/palette/PaletteGplWriter.js", "js/service/palette/reader/AbstractPaletteFileReader.js", @@ -242,6 +243,7 @@ "js/tools/transform/Crop.js", "js/tools/transform/Flip.js", "js/tools/transform/Rotate.js", + "js/tools/transform/PaletteApply.js", "js/tools/transform/TransformUtils.js", // Devtools