How to create a `pixelized' SVG image from a bitmap? - graphics

I have a 16x16 bitmap and want to create an SVG that contains 16x16 squares with the colors of the pixels of the image. Is there an easy way to achieve this?
My current thoughts go into the direction of using Python and PIL to read the bitmap image and dynamically create an SVG image file with the corresponding objects. But this feels a little clumsy and like reinventing the wheel.
Is there a better way to do this?

If you don't need the output to be SVG, I would suggest using an HTML5 Canvas where you can sample the pixels of the image client-side (using getImageData() on the context) and then draw your own up-scaled image. Or, if you need SVG, you could still use Canvas for the image sampling and then use procedurally-created <rect/> elements in SVG for each pixel.
I've written an example using just HTML Canvas so you can see how to do this. In short:
function drawPixelated(img,context,zoom,x,y){
if (!zoom) zoom=4; if (!x) x=0; if (!y) y=0;
if (!img.id) img.id = "__img"+(drawPixelated.lastImageId++);
var idata = drawPixelated.idataById[img.id];
if (!idata){
var ctx = document.createElement('canvas').getContext('2d');
ctx.width = img.width;
ctx.height = img.height;
ctx.drawImage(img,0,0);
idata = drawPixelated.idataById[img.id] = ctx.getImageData(0,0,img.width,img.height).data;
}
for (var x2=0;x2<img.width;++x2){
for (var y2=0;y2<img.height;++y2){
var i=(y2*img.width+x2)*4;
var r=idata[i ];
var g=idata[i+1];
var b=idata[i+2];
var a=idata[i+3];
context.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
context.fillRect(x+x2*zoom, y+y2*zoom, zoom, zoom);
}
}
};
drawPixelated.idataById={};
drawPixelated.lastImageId=0;
If you really need SVG involved, I'd be happy to write an example that dynamically generated that.
Edit: OK, I've created an SVG version just for fun and practice. :)
As an aside (from an initial misreading of your question) this demo file from ASVG3 their old SVG Examples Page shows how to use some complex compositing of many different effects to create pixelation on arbitrary vector data. Unfortunately the demo does not load in Chrome, having been hardwired to require the (now-discontinued) Adobe SVG Viewer.

Related

How do I carry over identical texture mapping when exporting to DAE?

I am able to open a 3DS file in MeshLab and when I export to Collada DAE format the textures are visible but they are not being projected onto the mesh in the same way as the preview in MeshLab. For example, the front/back faces of a cube would have the proper texture (suppose it's a polka dot) but the top and bottom have a striped look. How can I apply a single texture and have it appear as intended on all faces, like the imported model before I convert it?
This problem is a result of the end software being used to view the DAE file. It's not a problem with MeshLab.
For example, if loading the file into Away3D be sure to handle the texture materials using the TextureMaterial class instead of the simpler SinglePassMaterialBase such as what you might find in their example code. Here is what I use now, and it displays texture properly:
var material:TextureMaterial = cast(asset, TextureMaterial);
material.ambientColor = 0xffffff;
material.lightPicker = _lightPicker;
material.shadowMethod = new FilteredShadowMapMethod(_light);
material.lightPicker = _lightPicker;
material.gloss = 30;
material.specular = 1;
material.ambient = 1;
material.repeat = true;

How to apply transform to graphics in OpenFL

I'm converting a JavaScript library to Haxe. In this library, there is an animated effect constructed with many of shapes. So I used the OpenFL library to render shapes.
But now I have a technical problem with transformation.
Some of the shapes has the child shapes so it's transform should be applied to the child shapes too.
For example, please imagine shapeC is attached on shapeB and, shapeB and shapeD are also attached on shapeA. In this case, shapeB, shapeD should be transformed by both of transformA and their own transform and, shapeC also should by transformA, transformB and transformC.
To achieve this, is it a good solution to render the same level shapes in one graphic and apply the parent's transform to that graphic? (on above example, render shapeB and shapeD to one graphic and a apply transformA to that graphic)
I think it's not a good optimized solution to calculate the final transform from all parents transforms and apply that to all vertexes of that shape. Please tech me the best optimized solution for rendering.
Any suggestion will be welcome.
And if there is any confused things on this question, please pardon me and let me check.
You can use the Sprite class:
var parentShape = new Sprite ();
parentShape.graphics.beginFill (0xFF0000);
parentShape.graphics.drawRect (0, 0, 100, 100);
var childShape = new Sprite ();
childShape.graphics.beginFill (0x00FF00);
childShape.graphics.drawCircle (0, 0, 50);
childShape.x = 200;
childShape.y = 200;
parentShape.addChild (childShape);
addChild (parentShape);
Each shape will use its own canvas element, so if you create a lot of shapes, you may decide to flatten it into a single image when you are ready. This is possible using cacheAsBitmap or bitmapData.draw
parentShape.cacheAsBitmap = true;
...or
removeChild (parentShape);
var bitmapData = new BitmapData (Math.ceil (parentShape.width), Math.ceil (parentShape.height), true, 0);
bitmapData.draw (parentShape);
var bitmap = new Bitmap (bitmapData);
addChild (bitmap);

How to do svg scaling with Pixi

I've been trying to do SVG scaling with PIXI but the results are not really what I expected them to be. As you can see in the image, the debian logo, which is a SVG file, seems to be blurry and edgy. Am I writing my code wrong:
Refined from https://github.com/kevguy/D3-Svg-Comparison/blob/master/src/components/SvgCompare.vue:
// initialization
this.renderer = new PIXI.Application(800, 600, {backgroundColor: 0x1099bb})
document.getElementById('svg-canvas').appendChild(this.renderer.view)
this.container = new PIXI.Container()
this.stage = this.renderer.stage
this.stage.addChild(this.container)
// appending the svg file
const texture = PIXI.Texture.fromImage(this.chosenImage)
this.svg = new PIXI.Sprite(texture)
this.svg.anchor.x = 0.8
this.svg.anchor.y = 0.8
this.svg.position.x = 400
this.svg.position.y = 300
this.svg.scale.x = this.selectedScale
this.svg.scale.y = this.selectedScale
this.container.addChild(this.svg)
chosenImage is the svg file retrieved by using import * as choesnImage from 'the-file-path'
selectedScale is the selected scaling value which can be changed dynamically thanks to VueJS
You can check out my work here and its corresponding GitHub repo
The bunny logo is to verify when the scaling happens, it only applies to the SVG not the whole canvas.
According to this issue you need to load svg like this to generate scaled svg texture.
PIXI.Texture.fromImage(this.chosenImage, undefined, undefined, newVal)
And need to clean texture cache then create new texture for every new scale change.
PIXI.utils.clearTextureCache()
Modified SvgCompare.vue on gist

Can fabric.js parse raster graphic to "real" svg?

I have a question/problem with fabric.js - in my code the user can upload a picture, with filters he can convert it to a black/white image. When I export the picture with canvas.toSVG(); it exports a svg image, but it is no real vector graphic - it loses quality when scaling up.
function handleImage(e) {
var reader = new FileReader();
reader.onload = function (event) {
var img = new Image();
img.onload = function () {
var imgInstance = new fabric.Image(img, {
scaleX: 0.7,
scaleY: 0.7
})
canvas.add(imgInstance);
}
img.src = event.target.result;
} reader.readAsDataURL(e.target.files[0]);}
$('saveBtn').onclick = function() {
var filedata= canvas.toSVG(); // the SVG file is now in filedata
var locfile = new Blob([filedata], {type: "image/svg+xml;charset=utf-8"});
var locfilesrc = URL.createObjectURL(locfile);//mylocfile);
var dwn = document.getElementById('dwn');
dwn.innerHTML = "<a href=" + locfilesrc + " download='mysvg.svg'>Download</a>";}
What am I doing wrong?
There is no easy way to "parse" raster graphics to a vector image. Vector graphics include information for how to draw an image, while raster images only include the pixel data for how an image appears at a given size and resolution. That's enough for many purposes, but it means that while it's easy to go from vector to raster (just execute the instructions), it's not easy to go from raster to vector.
It is possible to "trace" the edges of a raster image to obtain vectors that can approximate the raster: in other words, a set of vector instructions that, for that particular resolution and depth, yields an image that is the same as the original raster (or something very like it). But there is no guarantee that these actually correspond in any way to the original vectors (if there are any original vectors at all). Usually there is no correspondence, in fact, unless your tracing algorithm is very specialized: for example, tracing images of a font to make a vector copy of that font. Because they don't correspond, there's no guarantee that the image will scale up the way you want it to: it'll scale, but things may enlarge in strange ways.
It is possible to implement tracing algorithms in JavaScript, by drawing the image into a <canvas> element, using getImageData() to grab the pixel information from that, and performing your operations on the pixel information. Doing this, though, is beyond the scope of this question.

d3.js geo - rendering svg path

I'd like to create choropleth map of Czech Republic. Inspired by this article http://bl.ocks.org/mbostock/4060606, I have created this
http://jsfiddle.net/1duds8tz/2/
var width = 960;
var height = 500;
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
var offset = [width / 2, height / 2];
var projection = d3.geo.mercator().scale(6000).center([15.474, 49.822]).translate(offset);
var path = d3.geo.path().projection(projection);
queue().defer(d3.json, "..map.geojson").await(ready);
function ready(error, reg) {
var group = svg.selectAll("g").data(reg.features).enter().append("g");
group.append("path").attr("d", path).attr("fill", "none").attr("stroke", "#222");
}
When I tried to fill svg path with some color, I ended on this
http://jsfiddle.net/1duds8tz/3/
group.append("path").attr("d", path).attr("fill", "red").attr("stroke", "#222");
There are odd values in path d attribute.
My GeoJSON data must be somehow faulty but I can't figure what is wrong.
Everything looks right here: https://gist.github.com/anonymous/4e51227dd83be8c2311d
Your geoJSON is corrupted and as a result your polygons are being drawn as the interiors of an infinitely bounded polygon. That's why when you attempt to give a fill to the path, it goes beyond the extent of the screen but still displays the border just fine. I tried to reverse the winding order of your coordinates array, and that seemed to fix all of them except for "Brno-venkov", which might be the source of your problems (especially given its administrative shape).
I'd suggest going back to where you created the original GeoJSON and try to re-export it with simplification. If you want to reverse the coordinates on your GeoJSON to correct the winding order, that's pretty simple:
geodata = d3.selectAll("path").data();
for (x in geodata) {geodata[x].geometry.coordinates[0] = geodata[x].geometry.coordinates[0].reverse()}
But this won't fix the problem polygon, nor will not reversing its coordinates.
In case you are familiar with svg manipulation you can try geojson2svg. This allows you manipulate svg in standard way but you have to code a little more. In case your application requires d3 for many other purpose then d3 is best solution.
I've got exactly the same problem with Mapzen's .geojson files.
.reverse()-ing isn't good enough, if you can't make sure all your data has the same winding order.
I solved it with this one:
https://www.npmjs.com/package/geojson-rewind
You'll need to have npm & require available
Install it, and save it to your project
npm i -g geojson-rewind
Import it, to make it useable
var rewind = require('geojson-rewind');
Use it on the data, in this case:
req = rewind(req);
Tip: If you are working with static data, you can do this only once on the console, and you're good to go.

Resources