colored SVG images

Color (fill) SVG images by pressing the `Shift` key.
This commit is contained in:
abakkk 2020-09-22 23:21:48 +02:00
parent 532b9242c9
commit 3c67bf3398
4 changed files with 51 additions and 17 deletions

View File

@ -603,7 +603,7 @@ var DrawingArea = new Lang.Class({
this._redisplay(); this._redisplay();
}, },
_startDrawing: function(stageX, stageY, eraser) { _startDrawing: function(stageX, stageY, shiftPressed) {
let [success, startX, startY] = this.transform_stage_point(stageX, stageY); let [success, startX, startY] = this.transform_stage_point(stageX, stageY);
if (!success) if (!success)
@ -617,7 +617,7 @@ var DrawingArea = new Lang.Class({
this.currentElement = new Elements.DrawingElement({ this.currentElement = new Elements.DrawingElement({
shape: this.currentTool, shape: this.currentTool,
color: this.currentColor, color: this.currentColor,
eraser: eraser, eraser: shiftPressed,
font: this.currentFont.copy(), font: this.currentFont.copy(),
// Translators: initial content of the text area // Translators: initial content of the text area
text: pgettext("text-area-content", "Text"), text: pgettext("text-area-content", "Text"),
@ -628,7 +628,7 @@ var DrawingArea = new Lang.Class({
this.currentElement = new Elements.DrawingElement({ this.currentElement = new Elements.DrawingElement({
shape: this.currentTool, shape: this.currentTool,
color: this.currentColor, color: this.currentColor,
eraser: eraser, colored: shiftPressed,
image: this.currentImage, image: this.currentImage,
points: [] points: []
}); });
@ -636,7 +636,7 @@ var DrawingArea = new Lang.Class({
this.currentElement = new Elements.DrawingElement({ this.currentElement = new Elements.DrawingElement({
shape: this.currentTool, shape: this.currentTool,
color: this.currentColor, color: this.currentColor,
eraser: eraser, eraser: shiftPressed,
fill: this.fill, fill: this.fill,
fillRule: this.currentFillRule, fillRule: this.currentFillRule,
line: { lineWidth: this.currentLineWidth, lineJoin: this.currentLineJoin, lineCap: this.currentLineCap }, line: { lineWidth: this.currentLineWidth, lineJoin: this.currentLineJoin, lineCap: this.currentLineCap },

View File

@ -771,7 +771,7 @@ const ImageElement = new Lang.Class({
return { return {
shape: this.shape, shape: this.shape,
color: this.color, color: this.color,
eraser: this.eraser, colored: this.colored,
transformations: this.transformations, transformations: this.transformations,
image: this.image, image: this.image,
preserveAspectRatio: this.preserveAspectRatio, preserveAspectRatio: this.preserveAspectRatio,
@ -791,7 +791,7 @@ const ImageElement = new Lang.Class({
return; return;
cr.save(); cr.save();
this.image.setCairoSource(cr, x, y, width, height, this.preserveAspectRatio); this.image.setCairoSource(cr, x, y, width, height, this.preserveAspectRatio, this.colored ? this.color.toJSON() : null);
cr.rectangle(x, y, width, height); cr.rectangle(x, y, width, height);
cr.fill(); cr.fill();
cr.restore(); cr.restore();
@ -813,14 +813,15 @@ const ImageElement = new Lang.Class({
_drawSvg: function(transAttribute) { _drawSvg: function(transAttribute) {
let points = this.points; let points = this.points;
let row = "\n "; let row = "\n ";
let attributes = this.eraser ? `class="eraser" ` : ''; let attributes = '';
if (points.length == 2) { if (points.length == 2) {
attributes += `fill="none"`; attributes += `fill="none"`;
let base64 = this.image.getBase64ForColor(this.colored ? this.color.toJSON() : null);
row += `<image ${attributes} x="${Math.min(points[0][0], points[1][0])}" y="${Math.min(points[0][1], points[1][1])}" ` + row += `<image ${attributes} x="${Math.min(points[0][0], points[1][0])}" y="${Math.min(points[0][1], points[1][1])}" ` +
`width="${Math.abs(points[1][0] - points[0][0])}" height="${Math.abs(points[1][1] - points[0][1])}"${transAttribute} ` + `width="${Math.abs(points[1][0] - points[0][0])}" height="${Math.abs(points[1][1] - points[0][1])}"${transAttribute} ` +
`preserveAspectRatio="${this.preserveAspectRatio ? 'xMinYMin' : 'none'}" ` + `preserveAspectRatio="${this.preserveAspectRatio ? 'xMinYMin' : 'none'}" ` +
`id="${this.image.displayName}" xlink:href="data:${this.image.contentType};base64,${this.image.base64}"/>`; `id="${this.image.displayName}" xlink:href="data:${this.image.contentType};base64,${base64}"/>`;
} }
return row; return row;

View File

@ -83,6 +83,18 @@ Object.keys(ThemedIconNames).forEach(key => {
}); });
}); });
const replaceColor = function (contents, color) {
if (contents instanceof Uint8Array)
contents = ByteArray.toString(contents);
else
contents = contents.toString();
return contents.replace(/fill(?=="transparent"|="none"|:transparent|:none)/gi, 'filll')
.replace(/fill="[^"]+"/gi, `fill="${color}"`)
.replace(/fill:[^";]+/gi, `fill:${color};`)
.replace(/filll/gi, 'fill');
};
// Wrapper around image data. If not subclassed, it is used when loading in the area an image element for a drawing file (.json) // Wrapper around image data. If not subclassed, it is used when loading in the area an image element for a drawing file (.json)
// and it takes { displayName, contentType, base64, hash } as params. // and it takes { displayName, contentType, base64, hash } as params.
var Image = new Lang.Class({ var Image = new Lang.Class({
@ -122,6 +134,14 @@ var Image = new Lang.Class({
this._base64 = base64; this._base64 = base64;
}, },
getBase64ForColor: function(color) {
if (!color || this.contentType != 'image/svg+xml')
return this.base64;
let contents = GLib.base64_decode(this.base64);
return GLib.base64_encode(replaceColor(contents, color));
},
// hash is not used // hash is not used
get hash() { get hash() {
if (!this._hash) if (!this._hash)
@ -133,7 +153,22 @@ var Image = new Lang.Class({
this._hash = hash; this._hash = hash;
}, },
get pixbuf() { getBytesForColor: function(color) {
if (!color || this.contentType != 'image/svg+xml')
return this.bytes;
let contents = this.bytes.get_data();
return new GLib.Bytes(replaceColor(contents, color));
},
getPixbuf: function(color) {
if (color) {
let stream = Gio.MemoryInputStream.new_from_bytes(this.getBytesForColor(color));
let pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null);
stream.close(null);
return pixbuf;
}
if (!this._pixbuf) { if (!this._pixbuf) {
let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes); let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes);
this._pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null); this._pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null);
@ -142,16 +177,16 @@ var Image = new Lang.Class({
return this._pixbuf; return this._pixbuf;
}, },
getPixbufAtScale: function(width, height) { getPixbufAtScale: function(width, height, color) {
let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes); let stream = Gio.MemoryInputStream.new_from_bytes(this.getBytesForColor(color));
let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, width, height, true, null); let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, width, height, true, null);
stream.close(null); stream.close(null);
return pixbuf; return pixbuf;
}, },
setCairoSource: function(cr, x, y, width, height, preserveAspectRatio) { setCairoSource: function(cr, x, y, width, height, preserveAspectRatio, color) {
let pixbuf = preserveAspectRatio ? this.getPixbufAtScale(width, height) let pixbuf = preserveAspectRatio ? this.getPixbufAtScale(width, height, color)
: this.pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR); : this.getPixbuf(color).scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR);
Gdk.cairo_set_source_pixbuf(cr, pixbuf, x, y); Gdk.cairo_set_source_pixbuf(cr, pixbuf, x, y);
} }
}); });
@ -552,7 +587,7 @@ var Jsons = {
var getDateString = function() { var getDateString = function() {
let date = GLib.DateTime.new_now_local(); let date = GLib.DateTime.new_now_local();
return `${date.format("%F")} ${date.format("%X")}`; return `${date.format("%F")} ${date.format("%T")}`;
}; };
var saveSvg = function(content) { var saveSvg = function(content) {

View File

@ -315,8 +315,6 @@ var DrawingMenu = new Lang.Class({
this.lineSection.actor.visible = !isText && !isImage; this.lineSection.actor.visible = !isText && !isImage;
this.fontSection.actor.visible = isText; this.fontSection.actor.visible = isText;
this.imageSection.actor.visible = isImage; this.imageSection.actor.visible = isImage;
this.colorItem.setSensitive(!isImage);
this.paletteItem.setSensitive(!isImage);
this.fillItem.setSensitive(!isText && !isImage); this.fillItem.setSensitive(!isText && !isImage);
this.fillSection.setSensitive(!isText && !isImage); this.fillSection.setSensitive(!isText && !isImage);