Подтвердить что ты не робот

Фильтр Alpha Mask EaselJS

Я новичок в Canvas. Я пытаюсь восстановить образы в этом файле EaselJS Alpha Mask, чтобы исходное изображение было четким и что вы paint размыто; в основном, обратная сторона демонстрации.

Я общался с ним часами, применяя фильтры к bitmap var и удаляя их из blur var. Все, что я делаю, просто не работает. Похоже, это было бы легко исправить просто переключение вещей, но это, похоже, не так. Не для меня в любом случае.

Есть ли у кого-нибудь пример этого или знаете, что делать? Я мог бы привести примеры кода того, что я сделал, но это в основном просто играет с такими вещами, как обезьяна на пишущей машинке.

Вот код на Github

Вот соответствующий код из их примера.

<script id="editable">
    var stage;
    var isDrawing;
    var drawingCanvas;
    var oldPt;
    var oldMidPt;
    var displayCanvas;
    var image;
    var bitmap;
    var maskFilter;
    var cursor;
    var text;
    var blur;

    function init() {
        examples.showDistractor();

        image = new Image();
        image.onload = handleComplete;
        image.src = "../_assets/art/flowers.jpg";

        stage = new createjs.Stage("testCanvas");
        //text = new createjs.Text("Loading...", "20px Arial", "#FFF");
        //text.set({x: stage.canvas.width / 2, y: stage.canvas.height - 40});
        //text.textAlign = "center";
    }

    function handleComplete() {
        examples.hideDistractor();
        createjs.Touch.enable(stage);
        stage.enableMouseOver();

        stage.addEventListener("stagemousedown", handleMouseDown);
        stage.addEventListener("stagemouseup", handleMouseUp);
        stage.addEventListener("stagemousemove", handleMouseMove);
        drawingCanvas = new createjs.Shape();
        bitmap = new createjs.Bitmap(image);

        blur = new createjs.Bitmap(image);
        blur.filters = [new createjs.BlurFilter(24, 24, 2), new createjs.ColorMatrixFilter(new createjs.ColorMatrix(60))];
        blur.cache(0, 0, 960, 400);

        //text.text = "Click and Drag to Reveal the Image.";

        stage.addChild(blur, text, bitmap);
        updateCacheImage(false);

        cursor = new createjs.Shape(new createjs.Graphics().beginFill("#FFFFFF").drawCircle(0, 0, 25));
        cursor.cursor = "pointer";

        stage.addChild(cursor);
    }

    function handleMouseDown(event) {
        oldPt = new createjs.Point(stage.mouseX, stage.mouseY);
        oldMidPt = oldPt;
        isDrawing = true;
    }

    function handleMouseMove(event) {
        cursor.x = stage.mouseX;
        cursor.y = stage.mouseY;

        if (!isDrawing) {
            stage.update();
            return;
        }

        var midPoint = new createjs.Point(oldPt.x + stage.mouseX >> 1, oldPt.y + stage.mouseY >> 1);

        drawingCanvas.graphics.setStrokeStyle(40, "round", "round")
                .beginStroke("rgba(0,0,0,0.2)")
                .moveTo(midPoint.x, midPoint.y)
                .curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);

        oldPt.x = stage.mouseX;
        oldPt.y = stage.mouseY;

        oldMidPt.x = midPoint.x;
        oldMidPt.y = midPoint.y;

        updateCacheImage(true);
    }

    function handleMouseUp(event) {
        updateCacheImage(true);
        isDrawing = false;
    }

    function updateCacheImage(update) {
        if (update) {
            drawingCanvas.updateCache();
        } else {
            drawingCanvas.cache(0, 0, image.width, image.height);
        }

        maskFilter = new createjs.AlphaMaskFilter(drawingCanvas.cacheCanvas);

        bitmap.filters = [maskFilter];
        if (update) {
            bitmap.updateCache(0, 0, image.width, image.height);
        } else {
            bitmap.cache(0, 0, image.width, image.height);
        }

        stage.update();
    }
</script>
4b9b3361

Ответ 1

Есть несколько шагов для этого. Большинство из них вы уже сделали:

1) Измените порядок добавления предметов на сцену. Так как вы хотите разглядеть размытие, добавьте их в обратном порядке. Это помещает размытие сверху.

stage.addChild(bitmap, text, blur);

2) Измените то, что кэшировано или updateCached в методе updateCacheImage:

if (update) {
    blur.updateCache(0, 0, image.width, image.height);
} else {
    blur.cache(0, 0, image.width, image.height);
}

Здесь вы, вероятно, споткнулись. Если вы установите фильтры на blurImage только на maskFilter, он не будет работать. МаскаФильтр работает, но будет удалены фильтры размытия и цвета, которые были применены. Чтобы добавить maskFilter, вы должны поместить его в массив с текущими фильтрами. Это мой подход, который гарантирует, что исходные 2 фильтра не повреждены, а maskFilter просто добавлен один раз:

blur.filters.length = 2; // Truncate the array to 2
blur.filters.push(maskFilter); // add the new filter

По-моему, этот эффект не так очевиден - так что вы можете увеличить непрозрачность кисти:

drawingCanvas.graphics.setStrokeStyle(40, "round", "round")
    .beginStroke("rgba(0,0,0,0.5)"); // From 0.2

Я был автором оригинальной демо-версии AlphaMaskFilter в EaselJS - рад, что вы сочли ее полезной и/или интересной!

Ответ 2

Чистый способ Javascript с использованием контекстного API Canvas 2D.

Вам нужно будет создать холст, загрузить изображение, создать изображение маски и размытие изображения. Я уже размыл изображение, так как не хотел писать размытие.

Следующие функции в объекте imageTools создают холст/изображения и загружают изображения. Обратите внимание, что холст и изображения взаимозаменяемы. В холсте нет src, и изображение не может быть нарисовано на appart из того, что они одинаковы. Я конвертирую все изображения в холст и присоединяю к ним контекст. Я также называю их изображениями.

/** ImageTools.js begin **/
var imageTools = (function () {
    var tools = {
        canvas : function (width, height) {  // create a blank image (canvas)
            var c = document.createElement("canvas");
            c.width = width;
            c.height = height;
            return c;
        },
        createImage : function (width, height) {
            var image = this.canvas(width, height);
            image.ctx = image.getContext("2d");
            return image;
        },
        loadImage : function (url, callback) {
            var image = new Image();
            image.src = url;
            image.addEventListener('load', callback);
            image.addEventListener('error', callback);
            return image;
        }
    };
    return tools;
})();

Затем я использую imageTools для загрузки изображений, которые мне нужны, и создания маски, когда у меня есть размер изображения, поскольку я согласую разрешение маски с разрешением изображения

// load the images and create the mask
var imageLoadedCount = 0;
var error = false;
var maskImage;
var flowerImage = imageTools.loadImage("http://www.createjs.com/demos/_assets/art/flowers.jpg", function (event) {
    if (event.type === "load") {
        imageLoadedCount += 1;
    } else {
        error = true;
    }
});
var flowerImageBlur = imageTools.loadImage("http://i.stack.imgur.com/3S5m8.jpg", function () {
    if (event.type === "load") {
        maskImage = imageTools.createImage(this.width, this.height);
        imageLoadedCount += 1;
    } else {
        error = true;
    }
});

Я использую requestAnimationFrame для создания функции рисования холста 60FPS, которая ждет загрузки изображений, а затем отображает 3 слоя на холст

   // ctx is the main canvas context.
   // drawImageCentered scales the image to fit. See Demo for code.

   // draw the unblured image that will appear at the top
   ctx.globalCompositeOperation = "source-over";
   drawImageCentered(ctx, flowerImage, cw, ch);
   drawText(ctx, "Click drag to blur the image via mask", 40 + Math.sin(time / 100), cw, ch - 30, "White");

   // Mask out the parts when the mask image has pixels
   ctx.globalCompositeOperation = "destination-out";
   drawImageCentered(ctx, maskImage, cw, ch);

   // draw the blured image only where the destination has been masked
   ctx.globalCompositeOperation = "destination-atop";
   drawImageCentered(ctx, flowerImageBlur, cw, ch);

Сначала он рисует изображение, которое появляется, если не видны маскированные пиксели. Затем нарисуйте текст для инструкций.

Далее идет маска, которая использует destination-out. Это означает, что для пикселей в маске, которые имеют альфa > 0, удаляют из адресата это количество альфа. Поэтому, если пиксель маски имеет альфа 50, а место назначения (холст) имеет альфа 255, тогда результат этого пикселя после рендеринга маски с destination-out будет 255 - 50 = 205. Это эффективно помещает дыры на холст, где когда-либо есть пиксели на маске.

Теперь мы можем заполнить отверстия размытым изображением и отобразить его с помощью destination-atop, что означает только рисование пикселей из источника (размытое изображение), где целевая альфа меньше 255

Это выполняется многослойная маскировка, все, что нам нужно, это нарисовать маску. Для этого мы просто слушаем события мыши, и если кнопка вниз, нарисуйте круг на маске, где находится мышь. Мой пример масштабировал изображения, поэтому есть немного дополнительной работы, но основы следующие:

// draws circle with gradient
function drawCircle(ctx, x, y, r) {
    var gr = ctx.createRadialGradient(x, y, 0, x, y, r)
        gr.addColorStop(1, "rgba(0,0,0,0)")
        gr.addColorStop(0.5, "rgba(0,0,0,0.08)")
        gr.addColorStop(0, "rgba(0,0,0,0.1)")
        ctx.fillStyle = gr;
    ctx.beginPath();
    ctx.arc(x, y, r, 0, Math.PI * 2);
    ctx.fill();
}
// draw a circle on the mask where the mouse is.
drawCircle(maskImage.ctx, mouse.x, mouse.y, 20);

Для демонстрации есть немного больше кода, чтобы все работало хорошо, но вы можете выбрать нужные вам биты.

var imageLoadedCount = 0;
var error = false;
var maskImage;
var flowerImage;
var flowerImageBlur;
/** ImageTools.js begin **/
var imageTools = (function () {
    var tools = {
        canvas : function (width, height) {  // create a blank image (canvas)
            var c = document.createElement("canvas");
            c.width = width;
            c.height = height;
            return c;
        },
        createImage : function (width, height) {
            var image = this.canvas(width, height);
            image.ctx = image.getContext("2d");
            return image;
        },
        loadImage : function (url, callback) {
            var image = new Image();
            image.src = url;
            image.addEventListener('load', callback);
            image.addEventListener('error', callback);
            return image;
        }
    };
    return tools;
})();




var mouse;
var demo = function(){
    /** fullScreenCanvas.js begin **/
    var canvas = (function(){
        var canvas = document.getElementById("canv");
        if(canvas !== null){
            document.body.removeChild(canvas);
        }
        // creates a blank image with 2d context
        canvas = document.createElement("canvas"); 
        canvas.id = "canv";    
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight; 
        canvas.style.position = "absolute";
        canvas.style.top = "0px";
        canvas.style.left = "0px";
        canvas.style.zIndex = 1000;
        canvas.ctx = canvas.getContext("2d"); 
        document.body.appendChild(canvas);
        return canvas;
    })();
    var ctx = canvas.ctx;
    
    /** fullScreenCanvas.js end **/
    /** MouseFull.js begin **/
    if(typeof mouse !== "undefined"){  // if the mouse exists 
        if( mouse.removeMouse !== undefined){
            mouse.removeMouse(); // remove previouse events
        }
    }else{
        var mouse;
    }
    var canvasMouseCallBack = undefined;  // if needed
    mouse = (function(){
        var mouse = {
            x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false,
            interfaceId : 0, buttonLastRaw : 0,  buttonRaw : 0,
            over : false,  // mouse is over the element
            bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
            getInterfaceId : function () { return this.interfaceId++; }, // For UI functions
            startMouse:undefined,
            mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
        };
        function mouseMove(e) {
            var t = e.type, m = mouse;
            m.x = e.offsetX; m.y = e.offsetY;
            if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; }
            m.alt = e.altKey;m.shift = e.shiftKey;m.ctrl = e.ctrlKey;
            if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1];
            } else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2];
            } else if (t === "mouseout") { m.buttonRaw = 0; m.over = false;
            } else if (t === "mouseover") { m.over = true;
            } else if (t === "mousewheel") { m.w = e.wheelDelta;
            } else if (t === "DOMMouseScroll") { m.w = -e.detail;}
            if (canvasMouseCallBack) { canvasMouseCallBack(mouse); }
            e.preventDefault();
        }
        function startMouse(element){
            if(element === undefined){
                element = document;
            }
            mouse.element = element;
            mouse.mouseEvents.forEach(
                function(n){
                    element.addEventListener(n, mouseMove);
                }
            );
            element.addEventListener("contextmenu", function (e) {e.preventDefault();}, false);
        }
        mouse.removeMouse = function(){
            if(mouse.element !== undefined){
                mouse.mouseEvents.forEach(
                    function(n){
                        mouse.element.removeEventListener(n, mouseMove);
                    }
                );
                canvasMouseCallBack = undefined;
            }
        }
        mouse.mouseStart = startMouse;
        return mouse;
    })();
    if(typeof canvas !== "undefined"){
        mouse.mouseStart(canvas);
    }else{
        mouse.mouseStart();
    }
    /** MouseFull.js end **/
    
    // load the images and create the mask
    if(imageLoadedCount === 0){
        imageLoadedCount = 0;
        error = false;
        maskImage;
        flowerImage =    imageTools.loadImage("http://www.createjs.com/demos/_assets/art/flowers.jpg", function (event) {
            if (event.type === "load") {
                imageLoadedCount += 1;
            } else {
                error = true;
            }
        })
        flowerImageBlur = imageTools.loadImage("http://i.stack.imgur.com/3S5m8.jpg", function () {
            if (event.type === "load") {
                maskImage = imageTools.createImage(this.width, this.height);
                imageLoadedCount += 1;
            } else {
                error = true;
            }
        })
    }
    // set up the canvas 
    var w = canvas.width;
    var h = canvas.height;
    var cw = w / 2;
    var ch = h / 2;


    // calculate time to download image using the MS algorithum. As this code is a highly gaurded secret I have obsficated it for your personal safty.
    var calculateTimeToGo= (function(){var b="# [email protected],Some time soon,Maybe Tomorrow.".replace(/Q/g,"@.,# ").split(","),r=Math.random,f=Math.floor,lc=0,pc=0,lt=0,lp=0;var cttg=function(a){if(lc===0){lc=100+r(r()*60);lt=f(r()*40);if(pc===0||r()<(lp/b.length)-0.2){lp=f(r()*b.length);pc=1+f(r()*10)}else{pc-=1}}else{lc-=1}a=lt;if(lp===0){a=lt;if(r()<0.01){lt-=1}}var s=b[lp].replace("#",a);if(a===1){s=s.replace("@","")}else{s=s.replace("@","s")}return s};return cttg})();    

    // draws circle with gradient
    function drawCircle(ctx, x, y, r) {
        var gr = ctx.createRadialGradient(x, y, 0, x, y, r)
            gr.addColorStop(1, "rgba(0,0,0,0)")
            gr.addColorStop(0.5, "rgba(0,0,0,0.08)")
            gr.addColorStop(0, "rgba(0,0,0,0.1)")
            ctx.fillStyle = gr;
        ctx.beginPath();
        ctx.arc(x, y, r, 0, Math.PI * 2);
        ctx.fill();
    }
    // draw text
    function drawText(ctx, text, size, x, y, c) {
        ctx.fillStyle = c;
        ctx.strokeStyle = "black";
        ctx.lineWidth = 5;
        ctx.lineJoin = "round";
        ctx.font = size + "px Arial Black";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        if (c !== "black") {
            ctx.strokeText(text, x, y + 1);
        }
        ctx.fillText(text, x, y);
    }
    // draw the image to fit the current canvas size
    function drawImageCentered(ctx, image, x, y) {
        var scale = Math.min(w / image.width, h / image.height);
        ctx.setTransform(scale, 0, 0, scale, cw, ch);
        ctx.drawImage(image, -image.width / 2, -image.height / 2);
        ctx.setTransform(1, 0, 0, 1, 0, 0);
    }
    // points for filling gaps between mouse moves.
    var lastMX,lastMY;
    // update function will try 60fps but setting will slow this down.    
    function update(time){
        ctx.setTransform(1, 0, 0, 1, 0, 0); // restore transform
        ctx.clearRect(0, 0, w, h); // clear rhe canvas
        // have the images loaded???
        if (imageLoadedCount === 2) {
            // draw the unblured image that will appear at the top
            ctx.globalCompositeOperation = "source-over";
            drawImageCentered(ctx, flowerImage, cw, ch);
            drawText(ctx, "Click drag to blur the image via mask", 20 + Math.sin(time / 100), cw, ch - 30, "White");
            // Mask out the parts when the mask image has pixels
            ctx.globalCompositeOperation = "destination-out";
            drawImageCentered(ctx, maskImage, cw, ch);
            // draw the blured image only where the destination has been masked
            ctx.globalCompositeOperation = "destination-atop";
            drawImageCentered(ctx, flowerImageBlur, cw, ch);

            // is the mouse down
            if (mouse.buttonRaw === 1) {
                // because image has been scaled need to get mouse coords on image
                var scale = Math.min(w / flowerImage.width, h / flowerImage.height);
                var x = (mouse.x - (cw - (maskImage.width / 2) * scale)) / scale;
                var y = (mouse.y - (ch - (maskImage.height / 2) * scale)) / scale;
                // draw circle on mask
                drawCircle(maskImage.ctx, x, y, 20);
                // if mouse is draging then draw some points between to fill the gaps
                if (lastMX !== undefined) {
                    drawCircle(maskImage.ctx, ((x + lastMX) / 2 + x) / 2, ((y + lastMY) / 2 + y) / 2, 20);
                    drawCircle(maskImage.ctx, (x + lastMX) / 2, (y + lastMY) / 2, 20);
                    drawCircle(maskImage.ctx, ((x + lastMX) / 2 + lastMX) / 2, ((y + lastMY) / 2 + lastMY) / 2, 20);
                }
                // save las mouse pos on image
                lastMX = x;
                lastMY = y;
            } else {
                // undefined last mouse pos
                lastMX = undefined;

            }
        } else {
            // Laoding images so please wait.
            drawText(ctx, "Please wait.", 40 + Math.sin(time / 100), cw, ch - 30, "White");
            drawText(ctx, "loading images... ", 12, cw, ch, "black")
            drawText(ctx, "ETA " + calculateTimeToGo(time), 14, cw, ch + 20, "black")
        }
        
        // if not restart the request animation frame
        if(!STOP){
            requestAnimationFrame(update);
        }else{
            var can = document.getElementById("canv");
            if(can !== null){
                document.body.removeChild(can);
            }        
            STOP = false;
            
        }
    }

    update();
   
}
var STOP = false; // flag to tell demo app to stop
function resizeEvent() {
    var waitForStopped = function () {
        if (!STOP) { // wait for stop to return to false
            demo();
            return;
        }
        setTimeout(waitForStopped, 200);
    }
    STOP = true;
    setTimeout(waitForStopped, 100);
}
window.addEventListener("resize", resizeEvent);
demo();
/** FrameUpdate.js end **/