sobota, 17 grudnia 2011

Notatki o Windows 8 - odc. 12

Aplikacje Metro w Java Script: używanie Canvas, transformacje w CSS3, przetwarzanie plików graficznych.

SVG and Canvas are effective graphics tools that can be used to create fast, lightweight vector graphics that display in web applications using JavaScript and HTML.

Canvas

If you don't set width and height attributes, the canvas appears at its default size of 300 pixels wide and 150 pixels high. By default, your app renders the canvas element with no content or border - it’s completely transparent. However, you can style it just like any normal image by specifying a margin, border, background, and so forth. To draw to the canvas, you obtain a rendering context from the canvas and use it to draw. There are different types of rendering contexts that enable you to draw in different ways. For example, there’s a 2-D context for 2-D graphics and a 3-D context for 3-D graphics.

fillRect (x, y, width, height), strokeRect(x, y, width, height), clearRect(x, y, width, height)

window.onload = draw;

function draw() {

 

    // Get the canvas element.

    var canvas = document.getElementById("canvas");

 

    // Specify a 2d drawing context.

    var context = canvas.getContext("2d");

 

    // Set the color to the RBG equivalent of gold.

    context.fillStyle = "rgb(255, 215, 0)";

 

    // Draw the rectangle.

    context.fillRect (15, 15, 55, 50);

 

    // Set the color to the RBG equivalent of purple and

    // set the opacity of the rectangle to 50 percent.

       

    context.fillStyle = "rgba(0, 0, 200, 0.5)"

 

    // Draw the rectangle.

    context.fillRect (40, 40, 45, 40);

}

Internally, paths are stored as a list of sub-paths (lines, arcs, etc) which together form a shape. Every time the beginPath method is called, the list is reset and we can start drawing new shapes.

Before drawing any lines you’ll need to call the moveTo function. The moveTo function doesn’t draw anything, it is like placing a pen or pencil on spot you want to begin drawing the triangle.

// Add the brown triangle.

context.beginPath();

context.moveTo(28, 20);

context.lineTo(78, 50);

context.lineTo(28, 78);

context.lineTo(28, 20);

         

// Set the color to the hexadecimal equivalent of brown.

// Omit this step and the triangle will render

// the default color of black.

context.fillStyle = "#996633";

 

// Draw the triangle.

context.fill();

context.arc(x, y, radius, startingAngle, endingAngle, antiClockwise);

// Start the path.

context.beginPath();

 

// Define the stroke color in RGB for blue.

context.strokeStyle = "rgb(0, 162, 232)";

 

// Define the circle using an arc.

context.arc(50, 50, 32, 0, 2 * Math.PI, true);

 

// draw the circle.

context.stroke();

Curves are used to draw complex shapes. There are two types of curves; the Bezier or cubic, and the quadratic. Both have a start and end point on the canvas however how the curve is drawn is based on a control point or points. The quadratic curve has one control point, while a cubic Bezier curve uses two control points. Curves are always contained in a path.

Quadratic curve

quadraticCurveTo(cp1x, cp1y,  X,  Y)

The four parameters are two pairs of X, Y coordinates, the first; cp1x and cp1y is the location of the control point on the curve and the second pair is the location of the end of the curve.

// Create the path.

context.beginPath();

 

// Set the starting point for the curve.

context.moveTo(75,25);

 

// Set each of the curves.       

context.quadraticCurveTo(10, 80, 40, 130);

context.quadraticCurveTo(30, 90, 50, 130);

context.quadraticCurveTo(50, 100, 70, 130);

context.quadraticCurveTo(80, 110, 100, 130);

context.quadraticCurveTo(120, 120, 140, 130);

 

// Set the color of the image to green.

context.fillStyle = "rgb(100, 200, 50)";

 

// Draw the image.

context.fill();

Bezier curve

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

The six parameters are three pairs of X, Y coordinates; cp1x and cp1y is the location of the first control point on the curve, cp2x and cp2y is the location of the second control point, and the third pair is the location of the end of the curve.

Timing animation

setInterval (animationFunction milliseconds);

setTimeout (animationFunction milliseconds);

Both setInterval and setTimeout methods have two parameters; the name of the function that draws the animation, and the number of milliseconds between each call to the animation function. Both methods call the animationFunction for the set number of milliseconds. The difference is that the setInterval method calls the animationFunction and waits for so many milliseconds. The setTimeout method executes after the milliseconds have passed.

Drawing

You'll need to clear the canvas before drawing each frame. There are various methods for clearing a canvas, or parts of an image, such as clearing certain areas using the globalCompositOperation property or by clipping paths using the clip method. The simplest way to clear a canvas is to use the clearRect method.

When drawing your image you might change some of the settings such as styles or transformations. If you want to use the original settings when you begin each redraw of the image, you can use the save method. The save and restore methods are used to save and retrieve the canvas state on a stack. The canvas state consists of all of the styles and transformations that have been applied. Every time the save method is called the current canvas state is saved on the stack. The restore method returns the lastest saved state from the stack.

The translate method is used to move the canvas and its origin to a different point in the canvas grid. This method takes two arguments; x is the amount the canvas is moved to the left or right, and y is the amount it's moved up or down.

It's a good idea to save the canvas state before doing any transformations as it is easier to call the restore method than having to do a reverse translation to return the canvas to its original state.

The rotate method is used to rotate the canvas around the current origin. This method has only one parameter and that’s the angle the canvas is rotated, measured in radians. The rotation moves clockwise, and the rotation center point is always the canvas origin (the top left corner). To move the center point you’ll need to move the canvas using the translate method.

image

window.onload = init;

 

function init(){

    setInterval(draw, 400);

}

 

function draw() {

    var context = document.getElementById("canvas").getContext("2d");

 

    // The clearRect method displays a single circle.

    // context.clearRect(0, 0, 160, 160);

 

    // Save the canvas state.

    context.save();        

 

    // centers the image on the canvas            

    context.translate(80, 80);

 

    // Rotate moves the spiraling circle around the canvas in a large orbit.

    var time = new Date();

    context.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );

 

    // Translate determines the location of the small circle.

    context.translate(50, 0); 

       

    // Rotate causes the circle to spiral as it circles around the canvas.

    context.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );

 

    // determines the size of the loop

    context.translate(0, 5); 

 

    // This draws the circle

    context.beginPath();

    context.arc(5, 5, 4, 0, Math.PI*2, true);

    context.stroke();

 

    // Restores the canvas to the previous state

    context.restore();

 

}

Transforming with CSS

Support for Cascading Style Sheets, Level 3 (CSS3) Transforms by Metro style apps enable you to manipulate one or more UI elements in 2-D and 3-D space at run time. Specifically, CSS3 2D Transforms enable you to translate (move), rotate, scale, and skew objects in 2-D space. CSS3 3D Transforms support these same transformations in 3-D space with the added ability to simulate a sense of depth through a perspective transform. When multiple transformations are specified individually, the results of the transformations are cumulative and depend on the order in which they are applied.

CSS3 2D Transforms

The -ms-transform-origin property can be used with -ms-transform to indicate the origin of transformation for an element. By default, the origin is the center (50% 50%) of the bounding box of the element being transformed with (0% 0%) indicating the top left corner where the x-axis points to the right and the y-axis points down.

-ms-transform: translate

  • translate(tx,[ty]) - If ty is not specified, the default value is zero. (Translation-value parameters can be either percentages or lengths.)
  • translateX(tx)
  • translateY(ty)

-ms-transform-origin has no effect on the results of the translate function.

   var transformString = 'translateX(' + tx + 'px) translateY(' + ty + 'px)';

   transformElement.style.msTransform = transformString;

-ms-transform: rotate

rotate(angle) rotates an element, with angle indicating the amount of rotation about the origin of the element. (Rotate-value parameters are numbers followed by an angle unit identifier: deg, grad, rad, or turn.)

    transformElement.style.msTransformOrigin = originX + '% ' + originY + '%';

    var transformString = 'rotate(' + angle + 'deg)';

    transformElement.style.msTransform = transformString;

-ms-transform: scale

  • scale(sx,[sy]) - If sy is not specified, the default value is equal to the first value.
  • scaleX(sx)
  • scaleX(sy)

    transformElement.style.msTransformOrigin = originX + '% ' + originY + '%';

 

    var transformString = 'scaleX(' + sx + ') scaleY(' + sy + ')';

    transformElement.style.msTransform = transformString;

-ms-transform: skew

  • skew(ax,[ay]) - If ay is not specified, the default value is zero. (Skew-value parameters are numbers followed by an angle unit identifier: deg, grad, rad, or turn.)
  • skewX(ax)
  • skewX(ay)

    transformElement.style.msTransformOrigin = originX.value + '% ' + originY.value + '%';

 

    var transformString = 'skewX(' + ax + 'deg) skewY(' + ay + 'deg)';

    transformElement.style.msTransform = transformString;

-ms-transform: matrix

  • matrix(a,b,c,d,e,f)

  var transformString = 'matrix(1, 0, 0, 1, ' + tx + ', ' + ty + ')';

  transformElement.style.msTransform = transformString;

Image files

The Windows.Graphics.Imaging namespace lets you decode images and then edit the pixels and metadata. Then you can encode the result in various formats.

When you set the src attribute the image's onload function is triggered and you can get the decoder.

Note  Using openAsync with the readWriteNoCopyOnWrite access mode give you the option to transcode later to the same file.

You have a decoder object you can use it to:

  • Read metadata from the image.
  • Get the pixel data from the image.
  • Create a encoder by transcoding.

You don't need to create a decoder to get an encoder. You can create an encoder and then set the metadata and pixel data from another source.

    var picker = new Windows.Storage.Pickers.FileOpenPicker();

picker.fileTypeFilter.append([".jpg"]);

 

picker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;

picker.pickSingleFileAsync().then(function (file) {

 

            if (!file) {

                // Display an error.

                return;

            }

            var objectUrl = window.URL.createObjectURL(file, false);

            var image = document.getElementById("imageHolder");

 

            image.onload = function () {

                // Call a function to actually create and use the decoder.

                createDecoder(file);

            }

 

            // This starts the image loading process and triggers the onLoad function.

            image.src = objectUrl;

}, function (error) {

            switch (error.number) {

                case -2003292336:

                    // ERR_COMPONENT_NOT_FOUND, display an error message.

                    break;

                default:

                    // Catch any other errors.

                    break;

            }

        });

    }

function createDecoder(file) {

         var accessMode = Windows.Storage.FileAccessMode.readWriteNoCopyOnWrite;

        

         return file.openAsync(accessMode).then(function (stream) {

            return Imaging.BitmapDecoder.createAsync(stream).then(function (decoder) {

                   // Do something with the decoder.

            });

         });

     }

Create encoder from decoder:

Imaging.BitmapEncoder.createForTranscodingAsync(stream, decoder).then(function (encoder) {

    // Do something with the encoder.

   

    // Calling flushAsync saves the file and you must recreate the encoder to do anything more.

    return encoder.flushAsync();

);}

This creates an encoder and saves to the same file stream that you used for decoding. When you call the flushAsync function the encoder saves and you must recreate the encoder to do anything more.

Read Image Metadata

How to use a BitmapDecoder object to read image metadata. You can read System.Photo Properties and use the Windows Imaging Component (WIC) metadata query language to get extended properties.

You can get the properties height, width, title, keywords, rating, and date-taken using the Windows.Storage.FileProperties class. If you don't need any other properties, you can get FileProperties object from a Windows.StorageFile without opening a stream.

You can request any of the System.Photo Properties from the decoder by specifying the name. Not all properties are supported for every format, the Photo Metadata Policies the supported image formats for each property listed on the individual policy pages.

(function (decoder) {

      var orientation;

      var model;

return decoder.bitmapProperties.getPropertiesAsync(["System.Photo.Orientation"])

     .then(function(retrievedProps) {

         // Save the property for later.

         orientation = retrievedProps.lookup("System.Photo.Orientation");

}, function (error) {

            // If the file format does not support properties or the requested property does not exist.

            switch (error.number) {

                case -2003292287:

                    // Display an error message.

                    break;

                default:

                    // Catch any other errors.

                    break;

            }

       });

Using the WIC metadata query language to look up properties works the same as using System.Photo Properties. You replace the property name with a query string, for more info see the Metadata Query Language Overview.

The 1st error case can be triggered for a few different reasons.

  • The image format does not support properties.
  • The image format does not support the specific property requested .
  • The property was not found in the image.

Unfortunately, all of these error codes are the same.

return decoder.bitmapProperties.getPropertiesAsync(["System.Photo.Orientation",

                                                         "System.Photo.CameraModel"])

     .then(function(retrievedProps) {

         orientation = retrievedProps.lookup("System.Photo.Orientation");

         model = retrievedProps.lookup("System.Photo.CameraModel");

     }

Get Pixel Data

How to use a BitmapDecoder object to get pixel data from an image. The pixels are packaged as a byte array and you can specify the format when you request the data.

(function (decoder) {

return decoder.getPixelDataAsync(

            Imaging.BitmapPixelFormat.R8G8B8A8Uint, // Force RGBA

            Imaging.AlphaMode.Straight,             // Force straight alpha

            new Imaging.BitmapTransform(),          // No transforms

            true,                                   // Respect EXIF orientation

            true                                    // Color convert to sRGB

            ).then(function (pixelDataProvider) {

                return pixelDataProvider.detachPixelData().then(function (pixels) {

                   // Loop through the pixels

                   for (var i = 0; i < width; i++) {

                      for (var j = 0; j < height; j++) {

                        // Zero out green and blue channels, leaving red + alpha

                        pixels[(i + j * width) * 4 + 1] = 0;

                        pixels[(i + j * width) * 4 + 2] = 0;

                      }

                   }

 

                   // Use or store the pixel data here.

 

                   pixels = null; // Deallocate the byte array.

             });

       });

});

You can call getPixelDataAsync with no arguments and it will return the pixel data in the original format. You should check the BitmapDecoder object to see what the pixel format and alpha mode are if you choose that option.

You can't call detachPixelData again or it will fail. You must get a new pixelDataProvider object to get the pixel data again.

You must deallocate the byte array of pixel data or there will be a memory leak because the pixelDataProvider does not deallocate it.

Encode an image

How to create an BitmapEncoder object and save its contents to a file. A BitmapEncoder decoder also lets you set metadata and pixel data in image files.

Imaging.BitmapEncoder.createForTranscodingAsync(stream, decoder).then(function (encoder) {

    // Do something with the encoder.

   

    // Calling flushAsync saves the file and you must recreate the encoder to do anything more.

    return encoder.flushAsync();

);}

var picker = new Windows.Storage.Pickers.FileSavePicker();

       

picker.fileTypeChoices.insert("JPEG file", [".jpg"]);

picker.defaultFileExtension = "jpg";

picker.suggestedFileName = "untitled";

 

// Set the encoder id to jpeg.

var encoderId = encoderId = Imaging.BitmapEncoder.jpegEncoderId;

var filename;

       

picker.pickSaveFileAsync().then(function (file) {

 

            if (!file) {

                // Display an error.

                return;

            }

 

            filename = file.fileName;

 

            return file.openAsync(Windows.Storage.FileAccessMode.readWrite);

        }).then(function (stream) {

return Imaging.BitmapEncoder.createAsync(encoderId, stream);

        }).then(function (encoder) {

 

             // Do something with the encoder here.

 

            // Save the image file. You must recreate the encoder to do anything more.

            return encoder.flushAsync();

        }).then(function () {

You can add more extensions to the file type filter, see Windows.Storage.Pickers for more info. Encoders are only available for some file types, see Windows.Imaging.BitmapEncoder for a list.

Write Image Metadata

How to use a BitmapEncoder object to write image metadata. You can write to System.Photo Properties and use the Windows Imaging Component (WIC) metadata query language to write extended properties.

Note  You can set the properties height, width, title, keywords, rating, and date-taken using the Windows.Storage.FileProperties class. If you don't need any other properties, you can get FileProperties object from a Windows.StorageFile without opening a stream.

function (encoder) {

encoder.savePropertiesAsync(["System.Photo.Orientation": 1]).then(function () {

Using the WIC metadata query language to set properties works the same as using System.Photo Properties. You just replace the property name with a query string.

encoder.savePropertiesAsync(["System.Photo.Orientation": 1], "System.Photo.CameraModel": "Camera Model 1"]);

c.d.n

Brak komentarzy: