0

I have an algorithm in JavaScript which I want to transform into Java.

Here is the JavaScript code:

    function downScaleCanvas(cv, scale) {
        if (!(scale < 1) || !(scale > 0)) throw ('scale must be a positive number <1 ');
        var sqScale = scale * scale; // square scale = area of source pixel within target
        var sw = cv.width; // source image width
        var sh = cv.height; // source image height
        var tw = Math.ceil(sw * scale); // target image width
        var th = Math.ceil(sh * scale); // target image height
        var sx = 0, sy = 0, sIndex = 0; // source x,y, index within source array
        var tx = 0, ty = 0, yIndex = 0, tIndex = 0; // target x,y, x,y index within target array
        var tX = 0, tY = 0; // rounded tx, ty
        var w = 0, nw = 0, wx = 0, nwx = 0, wy = 0, nwy = 0; // weight / next weight x / y
        // weight is weight of current source point within target.
        // next weight is weight of current source point within next target's point.
        var crossX = false; // does scaled px cross its current px right border ?
        var crossY = false; // does scaled px cross its current px bottom border ?
        var sBuffer = cv.getContext('2d').getImageData(0, 0, sw, sh).data; // source buffer 8 bit rgba
        var tBuffer = new Float32Array(4 * sw * sh); // target buffer Float32 rgb
        var sR = 0, sG = 0,  sB = 0; // source's current point r,g,b
        // untested !
        var sA = 0;  //source alpha    

        for (sy = 0; sy < sh; sy++) {
            ty = sy * scale; // y src position within target
            tY = 0 | ty;     // rounded : target pixel's y
            yIndex = 4 * tY * tw;  // line index within target array
            crossY = (tY != (0 | ty + scale)); 
            if (crossY) { // if pixel is crossing botton target pixel
                wy = (tY + 1 - ty); // weight of point within target pixel
                nwy = (ty + scale - tY - 1); // ... within y+1 target pixel
            }
            for (sx = 0; sx < sw; sx++, sIndex += 4) {
                tx = sx * scale; // x src position within target
                tX = 0 |  tx;    // rounded : target pixel's x
                tIndex = yIndex + tX * 4; // target pixel index within target array
                crossX = (tX != (0 | tx + scale));
                if (crossX) { // if pixel is crossing target pixel's right
                    wx = (tX + 1 - tx); // weight of point within target pixel
                    nwx = (tx + scale - tX - 1); // ... within x+1 target pixel
                }
                sR = sBuffer[sIndex    ];   // retrieving r,g,b for curr src px.
                sG = sBuffer[sIndex + 1];
                sB = sBuffer[sIndex + 2];
                sA = sBuffer[sIndex + 3];

                if (!crossX && !crossY) { // pixel does not cross
                    // just add components weighted by squared scale.
                    tBuffer[tIndex    ] += sR * sqScale;
                    tBuffer[tIndex + 1] += sG * sqScale;
                    tBuffer[tIndex + 2] += sB * sqScale;
                    tBuffer[tIndex + 3] += sA * sqScale;
                } else if (crossX && !crossY) { // cross on X only
                    w = wx * scale;
                    // add weighted component for current px
                    tBuffer[tIndex    ] += sR * w;
                    tBuffer[tIndex + 1] += sG * w;
                    tBuffer[tIndex + 2] += sB * w;
                    tBuffer[tIndex + 3] += sA * w;
                    // add weighted component for next (tX+1) px                
                    nw = nwx * scale;
                    tBuffer[tIndex + 4] += sR * nw; // not 3
                    tBuffer[tIndex + 5] += sG * nw; // not 4
                    tBuffer[tIndex + 6] += sB * nw; // not 5
                    tBuffer[tIndex + 7] += sA * nw; // not 6
                } else if (crossY && !crossX) { // cross on Y only
                    w = wy * scale;
                    // add weighted component for current px
                    tBuffer[tIndex    ] += sR * w;
                    tBuffer[tIndex + 1] += sG * w;
                    tBuffer[tIndex + 2] += sB * w;
                    tBuffer[tIndex + 3] += sA * w;
                    // add weighted component for next (tY+1) px                
                    nw = nwy * scale;
                    tBuffer[tIndex + 4 * tw    ] += sR * nw; // *4, not 3
                    tBuffer[tIndex + 4 * tw + 1] += sG * nw; // *4, not 3
                    tBuffer[tIndex + 4 * tw + 2] += sB * nw; // *4, not 3
                    tBuffer[tIndex + 4 * tw + 3] += sA * nw; // *4, not 3
                } else { // crosses both x and y : four target points involved
                    // add weighted component for current px
                    w = wx * wy;
                    tBuffer[tIndex    ] += sR * w;
                    tBuffer[tIndex + 1] += sG * w;
                    tBuffer[tIndex + 2] += sB * w;
                    tBuffer[tIndex + 3] += sA * w;
                    // for tX + 1; tY px
                    nw = nwx * wy;
                    tBuffer[tIndex + 4] += sR * nw; // same for x
                    tBuffer[tIndex + 5] += sG * nw;
                    tBuffer[tIndex + 6] += sB * nw;
                    tBuffer[tIndex + 7] += sA * nw;
                    // for tX ; tY + 1 px
                    nw = wx * nwy;
                    tBuffer[tIndex + 4 * tw    ] += sR * nw; // same for mul
                    tBuffer[tIndex + 4 * tw + 1] += sG * nw;
                    tBuffer[tIndex + 4 * tw + 2] += sB * nw;
                    tBuffer[tIndex + 4 * tw + 3] += sA * nw;
                    // for tX + 1 ; tY +1 px
                    nw = nwx * nwy;
                    tBuffer[tIndex + 4 * tw + 4] += sR * nw; // same for both x and y
                    tBuffer[tIndex + 4 * tw + 5] += sG * nw;
                    tBuffer[tIndex + 4 * tw + 6] += sB * nw;
                    tBuffer[tIndex + 4 * tw + 7] += sA * nw;
                }
            } // end for sx 
        } // end for sy

        // create result canvas
        var resCV = document.createElement('canvas');
        resCV.width = tw;
        resCV.height = th;
        var resCtx = resCV.getContext('2d');
        var imgRes = resCtx.getImageData(0, 0, tw, th);
        var tByteBuffer = imgRes.data;
        // convert float32 array into a UInt8Clamped Array
        var pxIndex = 0; //  
        for (sIndex = 0, tIndex = 0; pxIndex < tw * th; sIndex += 4, tIndex += 4, pxIndex++) {
            tByteBuffer[tIndex] = Math.ceil(tBuffer[sIndex]);
            tByteBuffer[tIndex + 1] = Math.ceil(tBuffer[sIndex + 1]);
            tByteBuffer[tIndex + 2] = Math.ceil(tBuffer[sIndex + 2]);
            tByteBuffer[tIndex + 3] = Math.ceil(tBuffer[sIndex + 3]);
        }
        // writing result to canvas.
        resCtx.putImageData(imgRes, 0, 0);
        return resCV;
    }

Here is what I did so far in Java:

    public static final CanvasElement scaleCanvas(CanvasElement cv, double scale) {

        if (!(scale < 1) || !(scale > 0)) {
//          throw new Exception("scale must be a positive number <1 ");
        }

        double sqScale = scale * scale; // square scale = area of source pixel within target
        int sw = cv.getWidth(); // source image width
        int sh = cv.getHeight(); // source image height
        double tw = Math.ceil(sw * scale); // target image width
        double th = Math.ceil(sh * scale); // target image height
        int sx = 0, sy = 0, sIndex = 0; // source x,y, index within source array
        double tx = 0, ty = 0; 
        int yIndex = 0, tIndex = 0; // target x,y, x,y index within target array
        double tX = 0, tY = 0; // rounded tx, ty
        double w = 0, nw = 0, wx = 0, nwx = 0, wy = 0, nwy = 0; // weight / next weight x / y
        // weight is weight of current source point within target.
        // next weight is weight of current source point within next target's point.
        boolean crossX = false; // does scaled px cross its current px right border ?
        boolean crossY = false; // does scaled px cross its current px bottom border ?
        CanvasPixelArray sBuffer = cv.getContext2d().getImageData(0, 0, sw, sh).getData(); // source buffer 8 bit rgba
        Float32Array tBuffer = TypedArrays.createFloat32Array(4 * sw * sh);

        double sR = 0, sG = 0,  sB = 0; // source's current point r,g,b
        // untested !
        double sA = 0;  //source alpha    

        for (sy = 0; sy < sh; sy++) {
             GWT.log("sy: "+sy+" sh: "+sh);

            ty = sy * scale; // y src position within target
            tY = (long)ty;     // rounded : target pixel's y                            // ?????
            yIndex = (int)Math.floor(4 * tY * tw);  // line index within target array
            crossY = (tY != ( (long)(ty + scale) ));                                                    // ?????
            if (crossY) { // if pixel is crossing botton target pixel
                wy = (tY + 1 - ty); // weight of point within target pixel
                nwy = (ty + scale - tY - 1); // ... within y+1 target pixel
            }
            for (sx = 0; sx < sw; sx++, sIndex += 4) {
                tx = sx * scale; // x src position within target
                tX = (long)tx;    // rounded : target pixel's x                             // ?????
                tIndex = (int)Math.floor(yIndex + tX * 4); // target pixel index within target array                    // ?????
                crossX = (tX != ((int)Math.floor(tx + scale)));
                if (crossX) { // if pixel is crossing target pixel's right
                    wx = (tX + 1 - tx); // weight of point within target pixel
                    nwx = (tx + scale - tX - 1); // ... within x+1 target pixel
                }
                sR = sBuffer.get(sIndex);   // retrieving r,g,b for curr src px.
                sG = sBuffer.get(sIndex + 1);
                sB = sBuffer.get(sIndex + 2);
                sA = sBuffer.get(sIndex + 3);

                if (!crossX && !crossY) { // pixel does not cross
                    // just add components weighted by squared scale.

                    tBuffer.set(tIndex    , (float)(tBuffer.get(tIndex)     + sR * sqScale));
                    tBuffer.set(tIndex + 1, (float)(tBuffer.get(tIndex + 1) + sG * sqScale));
                    tBuffer.set(tIndex + 2, (float)(tBuffer.get(tIndex + 2) + sB * sqScale));
                    tBuffer.set(tIndex + 3, (float)(tBuffer.get(tIndex + 3) + sA * sqScale));
                } else if (crossX && !crossY) { // cross on X only
                    w = wx * scale;
                    // add weighted component for current px
                    tBuffer.set(tIndex    , (float)(tBuffer.get(tIndex)     + sR * w));
                    tBuffer.set(tIndex + 1, (float)(tBuffer.get(tIndex + 1) + sG * w));
                    tBuffer.set(tIndex + 2, (float)(tBuffer.get(tIndex + 2) + sB * w));
                    tBuffer.set(tIndex + 3, (float)(tBuffer.get(tIndex + 3) + sA * w));

                    // add weighted component for next (tX+1) px                
                    nw = nwx * scale;
                    tBuffer.set(tIndex + 4, (float)(tBuffer.get(tIndex + 4) + sR * nw));        // not 3 
                    tBuffer.set(tIndex + 5, (float)(tBuffer.get(tIndex + 5) + sG * nw));        // not 4
                    tBuffer.set(tIndex + 6, (float)(tBuffer.get(tIndex + 6) + sB * nw));        // not 5
                    tBuffer.set(tIndex + 7, (float)(tBuffer.get(tIndex + 7) + sA * nw));        // not 6
                } else if (crossY && !crossX) { // cross on Y only
                    w = wy * scale;
                    // add weighted component for current px
                    tBuffer.set(tIndex    , (float)(tBuffer.get(tIndex)     + sR * w));
                    tBuffer.set(tIndex + 1, (float)(tBuffer.get(tIndex + 1) + sG * w));
                    tBuffer.set(tIndex + 2, (float)(tBuffer.get(tIndex + 2) + sB * w));
                    tBuffer.set(tIndex + 3, (float)(tBuffer.get(tIndex + 3) + sA * w));

                    // add weighted component for next (tY+1) px                
                    nw = nwy * scale;
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw)    , (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw))     + sR * nw));    // *4, not 3
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 1), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 1)) + sG * nw));    // *4, not 3
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 2), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 2)) + sB * nw));    // *4, not 3
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 3), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 3)) + sA * nw));    // *4, not 3
                } else { // crosses both x and y : four target points involved
                    // add weighted component for current px
                    w = wx * wy;
                    tBuffer.set(tIndex    , (float)(tBuffer.get(tIndex) + sR * w));
                    tBuffer.set(tIndex + 1, (float)(tBuffer.get(tIndex + 1) + sG * w));
                    tBuffer.set(tIndex + 2, (float)(tBuffer.get(tIndex + 2) + sB * w));
                    tBuffer.set(tIndex + 3, (float)(tBuffer.get(tIndex + 3) + sA * w));
                    // for tX + 1; tY px
                    nw = nwx * wy;
                    tBuffer.set(tIndex + 4, (float)(tBuffer.get(tIndex + 4) + sR * nw));    // same for x
                    tBuffer.set(tIndex + 5, (float)(tBuffer.get(tIndex + 5) + sG * nw));
                    tBuffer.set(tIndex + 6, (float)(tBuffer.get(tIndex + 6) + sB * nw));
                    tBuffer.set(tIndex + 7, (float)(tBuffer.get(tIndex + 7) + sA * nw));
                    // for tX ; tY + 1 px
                    nw = wx * nwy;
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw)    , (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw))     + sR * nw));    // same for mul
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 1), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 1)) + sG * nw));
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 2), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 2)) + sB * nw));
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 3), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 3)) + sA * nw));
                    // for tX + 1 ; tY +1 px
                    nw = nwx * nwy;
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 4), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 4)) + sR * nw)); // same for both x and y
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 5), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 5)) + sG * nw));
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 6), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 6)) + sB * nw));
                    tBuffer.set((int)Math.floor(tIndex + 4 * tw + 7), (float)(tBuffer.get((int)Math.floor(tIndex + 4 * tw + 7)) + sA * nw));
                }
            } // end for sx 
        } // end for sy


        // create result canvas
        Canvas resCV = Canvas.createIfSupported();
        resCV.getCanvasElement().setWidth((int)Math.floor(tw));
        resCV.getCanvasElement().setHeight((int)Math.floor(th));
        Context2d resCtx = resCV.getContext2d();
        ImageData imgRes = resCtx.getImageData(0, 0, tw, th);
        CanvasPixelArray tByteBuffer = imgRes.getData();
        // convert float32 array into a UInt8Clamped Array
        int pxIndex = 0; 
        for (sIndex = 0, tIndex = 0; pxIndex < tw * th; sIndex += 4, tIndex += 4, pxIndex++) {
            tByteBuffer.set(tIndex, (int)Math.ceil(tBuffer.get(sIndex)));
            tByteBuffer.set(tIndex + 1, (int)Math.ceil(tBuffer.get(sIndex + 1)));
            tByteBuffer.set(tIndex + 2, (int)Math.ceil(tBuffer.get(sIndex + 2)));
            tByteBuffer.set(tIndex + 3, (int)Math.ceil(tBuffer.get(sIndex + 3)));
        }
        // writing result to canvas.
        resCtx.putImageData(imgRes, 0, 0);

        return resCV.getCanvasElement();

    }

I am not sure how to write an expression like thit tY = 0 | ty in Java. I am also not sure if canvas.width(value); transforms into canvas.setCoordinateSpaceWidth(value);

Edit:

Is (0 | ty + scale) in JS is ((long)ty + scale) in Java?

What is the translation of

var sBuffer = cv.getContext('2d').getImageData(0, 0, sw, sh).data 

is it CanvasPixelArray

CanvasPixelArray sBuffer = cv.getContext2d().getImageData(0, 0, sw, sh).getData()

Can someone help me with the translation?

Edit: It turns out that my Java code is very slow. I cannot run it in dev mode. In prod mode it is event slower than the JavaScript code. Why is this code slower?

3
  • 2
    Why you want to convert it into Java? You can call JavaScript function form Java Code in GWT. Commented Apr 16, 2014 at 22:12
  • For more info have a look at Calling Javascript function of .js file from java GWT code Commented Apr 16, 2014 at 22:18
  • 1
    @Braj I know how to use JSNI but I want pure Java. That way the GWT compile can optimize the code. Commented Apr 16, 2014 at 22:53

1 Answer 1

0

0 | tY is rounding a JS Number (equivalent to a Java double) to an integer value; equivalent to Math.floor(tY) or (long) tY (int is too small for all integer values that can be expressed in a double, but might be OK in this specific case).

canvas.width maps to Canvas#getCoordinateSpaceWidth, as you can see in GWT sources.

Finally, new Float32Array(n) maps to TypedArrays.createFloat32Array.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.