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

View File

@ -771,7 +771,7 @@ const ImageElement = new Lang.Class({
return {
shape: this.shape,
color: this.color,
eraser: this.eraser,
colored: this.colored,
transformations: this.transformations,
image: this.image,
preserveAspectRatio: this.preserveAspectRatio,
@ -791,7 +791,7 @@ const ImageElement = new Lang.Class({
return;
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.fill();
cr.restore();
@ -813,14 +813,15 @@ const ImageElement = new Lang.Class({
_drawSvg: function(transAttribute) {
let points = this.points;
let row = "\n ";
let attributes = this.eraser ? `class="eraser" ` : '';
let attributes = '';
if (points.length == 2) {
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])}" ` +
`width="${Math.abs(points[1][0] - points[0][0])}" height="${Math.abs(points[1][1] - points[0][1])}"${transAttribute} ` +
`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;

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)
// and it takes { displayName, contentType, base64, hash } as params.
var Image = new Lang.Class({
@ -122,6 +134,14 @@ var Image = new Lang.Class({
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
get hash() {
if (!this._hash)
@ -133,7 +153,22 @@ var Image = new Lang.Class({
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) {
let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes);
this._pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null);
@ -142,16 +177,16 @@ var Image = new Lang.Class({
return this._pixbuf;
},
getPixbufAtScale: function(width, height) {
let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes);
getPixbufAtScale: function(width, height, color) {
let stream = Gio.MemoryInputStream.new_from_bytes(this.getBytesForColor(color));
let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, width, height, true, null);
stream.close(null);
return pixbuf;
},
setCairoSource: function(cr, x, y, width, height, preserveAspectRatio) {
let pixbuf = preserveAspectRatio ? this.getPixbufAtScale(width, height)
: this.pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR);
setCairoSource: function(cr, x, y, width, height, preserveAspectRatio, color) {
let pixbuf = preserveAspectRatio ? this.getPixbufAtScale(width, height, color)
: this.getPixbuf(color).scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR);
Gdk.cairo_set_source_pixbuf(cr, pixbuf, x, y);
}
});
@ -552,7 +587,7 @@ var Jsons = {
var getDateString = function() {
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) {

View File

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