adrien54
adrien54

Reputation: 1640

Pixelated image in Fabric.js with sliceHack Resize filter

I can't find a way to use the Resize filter (slideHack) without getting a pixelated image sometimes. I use fabric.js 1.7.2

enter image description here

  1. I just added the image in the canvas

    fabric.Image.fromURL(url, function(oImg) 
    {
        var scaling = 0.2;
        var rFilter = new fabric.Image.filters.Resize({
            resizeType: 'sliceHack'
        });
        oImg.resizeFilters.push(rFilter);
        oImg.applyFilters();
    
        oImg.set({
            left:   300, 
            top:    300, 
            scaleX: scaling, 
            scaleY: scaling
        });
    
        canvas.add(oImg);
        canvas.renderAll();
    });
    
  2. When I click on the image or resize it manually, the edges get smooth.

  3. When I apply a Tint Filter, it is pixelated again

I can't find the function triggered to smooth the edges...

Thanks for your help.

Upvotes: 1

Views: 2812

Answers (2)

Long Nguyen
Long Nguyen

Reputation: 10956

I think that pixelated problem was fixed in version 2. Edge was smoother than version 1.72

Notice breaking changes in v2:

Another breaking change is that the .resizeFilters is no more an array of resize filter. Is a single resizeFilter that you can use when the object is scaled on the canvas.

image.resizeFilter = new fabric.Image.filters.ResizeFilter({type: 'hermite'});

var canvas = new fabric.Canvas('c');
var ctx = canvas.getContext("2d");
var url = 'http://i.imgur.com/a47Yxsb.png';
var imgWidth = 1024;

function performScaling() {
  // Get scaling factor
  var scaling =  parseFloat(document.getElementById("txtScaling").value);

  canvas.clear();

  // As suggested in Fabric.js introduction
  fabric.Image.fromURL(url, function (oImg) {
    oImg.set({
      left: imgWidth * 1.1 * scaling,
      top: 16
    });
    oImg.scale(scaling);
    oImg.resizeFilter = new fabric.Image.filters.Resize({
        resizeType: 'sliceHack'
    });
    oImg.applyResizeFilters();
    canvas.add(oImg);
    canvas.renderAll();

    // canvas.setBackgroundImage(
    //   oImg,
    //   () => {
    //     canvas.renderAll();
    //   },
    // );
    
  },{ crossOrigin: 'Anonymous' });
  
  // Add labels
  
  canvas.add(new fabric.Text('Not pixelated', {
    fontFamily: "Arial",
    fontSize: 12,
    left: 1.58 * imgWidth * scaling,
    top: 0,
    fill: 'black',
    originX: 'center'
  }));  
}
#divScaling
{
  display: inline-block;
  vertical-align: middle;
  margin: 6px 32px 16px 0px;
  vertical-align: top;
}

#txtScaling
{
  width: 70px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.4/fabric.min.js"></script>
<div id="divScaling">
  <label for="txtScaling">Scaling factor: </label>
  <input id="txtScaling" type="text" value="0.2" />
  <button onclick="performScaling()">
  Scale
  </button>
</div>
<img src="http://i.imgur.com/a47Yxsb.png" style="width: 30px;height: 30px;"/>

<canvas id="c" width="600" height="400"></canvas>

Upvotes: 1

Martin Parenteau
Martin Parenteau

Reputation: 73731

The document Introduction to Fabric.js, Part 2 suggests the following syntax, which seems to solve the issue:

fabric.Image.fromURL('pug.jpg', function(img) {

  // add filter
  img.filters.push(filter);

  // apply filters and re-render canvas when done
  img.applyFilters(canvas.renderAll.bind(canvas));

  // add image onto canvas
  canvas.add(img);
});

The result is compared to your original method in the code snippet below. In order to make it work, I also added { crossOrigin: 'Anonymous' } (see the comments to this answer by AndreaBogazzi).

var canvas = new fabric.Canvas('c');
var ctx = canvas.getContext("2d");
var url = 'http://i.imgur.com/a47Yxsb.png';
var imgWidth = 770;

function performScaling() {
  // Get scaling factor
  var scaling =  parseFloat(document.getElementById("txtScaling").value);

  canvas.clear();

  // With original method
  fabric.Image.fromURL(url, function(oImg) 
  {
    var rFilter = new fabric.Image.filters.Resize({
      resizeType: 'sliceHack'
    });
    oImg.resizeFilters.push(rFilter);
    oImg.applyFilters();

    oImg.set({
      left:   0, 
      top:    16, 
      scaleX: scaling, 
      scaleY: scaling
    });

    canvas.add(oImg);
    canvas.renderAll();
  });

  // As suggested in Fabric.js introduction
  fabric.Image.fromURL(url, function (oImg) {
      oImg.filters.push(new fabric.Image.filters.Resize({
          resizeType: 'sliceHack', scaleX: scaling , scaleY: scaling 
      }));
    oImg.set({
      left: imgWidth * 1.1 * scaling,
      top: 16
    });
    oImg.applyFilters(canvas.renderAll.bind(canvas));  
    canvas.add(oImg);
  },{ crossOrigin: 'Anonymous' });
  
  // Add labels
  canvas.add(new fabric.Text('Pixelated', {
    fontFamily: 'Arial',
    fontSize: 12,
    left: 0.48 * imgWidth * scaling,
    top: 0,
    fill: 'black',
    originX: 'center'
  }));  
  
  canvas.add(new fabric.Text('Not pixelated', {
    fontFamily: "Arial",
    fontSize: 12,
    left: 1.58 * imgWidth * scaling,
    top: 0,
    fill: 'black',
    originX: 'center'
  }));  
}
#divScaling
{
  display: inline-block;
  vertical-align: middle;
  margin: 6px 32px 16px 0px;
  vertical-align: top;
}

#txtScaling
{
  width: 70px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.2/fabric.min.js"></script>
<div id="divScaling">
  <label for="txtScaling">Scaling factor: </label>
  <input id="txtScaling" type="text" value="0.2" />
  <button onclick="performScaling()">
  Scale
  </button>
</div>
<img src="http://i.imgur.com/a47Yxsb.png" style="width: 30px;height: 30px;"/>

<canvas id="c" width="600" height="400"></canvas>

Upvotes: 3

Related Questions