/* eyeOS Spice Web Client Copyright (c) 2015 eyeOS S.L. Contact Jose Carlos Norte (jose@eyeos.com) for more information about this software. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License version 3 along with this program in the file "LICENSE". If not, see . See www.eyeos.org for more details. All requests should be sent to licensing@eyeos.org The interactive user interfaces in modified source and object code versions of this program must display Appropriate Legal Notices, as required under Section 5 of the GNU Affero General Public License version 3. In accordance with Section 7(b) of the GNU Affero General Public License version 3, these Appropriate Legal Notices must retain the display of the "Powered by eyeos" logo and retain the original copyright notice. If the display of the logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices must display the words "Powered by eyeos" and retain the original copyright notice. */ wdi.RasterEngine = $.spcExtend(wdi.EventObject.prototype, { init: function(c) { this.clientGui = c.clientGui; }, drawCanvas: function(spiceMessage) { return this.clientGui.drawCanvas(spiceMessage); }, removeCanvas: function(spiceMessage) { return this.clientGui.removeCanvas(spiceMessage); }, invalList: function(spiceMessage) { var items = spiceMessage.args.items; var item = null; for(var i in items) { item = items[i]; wdi.ImageCache.delImage(item.id); } }, handleStreamCreate: function(spiceMessage) { var stream = spiceMessage.args; stream.computedBox = wdi.graphics.getBoxFromSrcArea(stream.rect); wdi.Stream.addStream(spiceMessage.args.id, stream); }, handleStreamData: function(spiceMessage) { var imageData = spiceMessage.args.data; //jpeg string encoded var stream = wdi.Stream.getStream(spiceMessage.args.id); //recover the stream var context = this.clientGui.getContext(stream.surface_id); var img = wdi.GlobalPool.create('Image'); //auto-release pool wdi.ExecutionControl.sync = true; var url; img.onload = function() { URL.revokeObjectURL(url); var box = stream.computedBox; // we only rotate the stream if spice tells us so through the TOP_DOWN flag mask if (!stream.flags & wdi.SpiceStreamFlags.SPICE_STREAM_FLAGS_TOP_DOWN) { var offsetX = box.x + (this.width/2); var offsetY = box.y + (this.height/2); context.save(); context.translate(offsetX, offsetY); context.rotate(Math.PI); context.scale(-1,1); context.drawImage(this, box.x-offsetX, box.y-offsetY, box.width, box.height); context.restore(); } else { context.drawImage(this, box.x, box.y, box.width, box.height); } }; img.onerror = function() { URL.revokeObjectURL(url) }; url = wdi.SpiceObject.bytesToURI(imageData); img.src = url; }, handleStreamClip: function(spiceMessage) { wdi.Stream.clip(spiceMessage.args.id, spiceMessage.args.clip) }, handleStreamDestroy: function(spiceMessage) { wdi.Stream.deleteStream(spiceMessage.args.id); }, drawRop3: function(spiceMessage) { var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.base.box); var context = this.clientGui.getContext(spiceMessage.args.base.surface_id); var destImg = context.getImageData(box.x, box.y, box.width, box.height); var clientGui = this.clientGui; var brush = spiceMessage.args.brush; var rop = spiceMessage.args.rop_descriptor; var srcArea = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area); wdi.graphics.getImageFromSpice(spiceMessage.args.src_image.imageDescriptor, spiceMessage.args.src_image.data, this.clientGui, function (sourceCanvas) { if (sourceCanvas) { //Get source image data (image coming from the packet) var sourceContext = sourceCanvas.getContext('2d'); var srcImg = sourceContext.getImageData(srcArea.x, srcArea.y, srcArea.width, srcArea.height); var srcImgData = srcImg.data; //this //Get pattern image data //brush var tmpcanvas = wdi.graphics.getNewTmpCanvas(box.width, box.height); var tmpcontext = tmpcanvas.getContext('2d'); var brushBox = { width: box.width, height: box.height, x: 0, y: 0 }; wdi.graphics.setBrush(clientGui, tmpcontext, brush, brushBox, wdi.SpiceRopd.SPICE_ROPD_OP_PUT);//Without alpha? var pattern = tmpcontext.getImageData(0, 0, box.width, box.height); var patImgData = pattern.data; //this //Get dest image data var destImgData = destImg.data; //Get result image data tmpcanvas = wdi.graphics.getNewTmpCanvas(box.width, box.height); tmpcontext = tmpcanvas.getContext('2d'); var result = tmpcontext.createImageData(box.width, box.height); var resultData = result.data; if ((srcImg.width != pattern.width || srcImg.width != destImg.width) || (srcImg.height != pattern.height || srcImg.height != destImg.height)) { //TODO: resize } //Do the Ternary Raster Operation var length = destImgData.length;//Could be anyone var func = wdi.Rop3[rop]; for (var i = 0;i 30 && alpha === 255) { //formula from reactos, this is premultiplied alpha values result[px] = ((rD * (255 - aS)) / 255 + rS) & 0xff; result[px+1] = ((gD * (255 - aS)) / 255 + gS) & 0xff; result[px+2] = ((bD * (255 - aS)) / 255 + bS) & 0xff; } else { //homemade blend function, this is the typical blend function simplified result[px] = (( (rS*aS)+(rD*(255-aS)) ) / 255) & 0xff; result[px+1] = (( (gS*aS)+(gD*(255-aS)) ) / 255) & 0xff; result[px+2] = (( (bS*aS)+(bD*(255-aS)) ) / 255) & 0xff; } result[px+3] = 255; } imageResult.getContext('2d').putImageData(resultImageData, 0, 0); this.drawClip(imageResult, box_dest, this.clientGui.getContext(surface_id)); }, this); }, drawWhiteness: function(spiceMessage) { //TODO: mask var base = spiceMessage.args.base; var context = this.clientGui.getContext(base.surface_id); var box = wdi.graphics.getBoxFromSrcArea(base.box); context.fillStyle = "white"; context.fillRect(box.x, box.y, box.width, box.height); }, drawBlackness: function(spiceMessage) { //TODO: mask var base = spiceMessage.args.base; var context = this.clientGui.getContext(base.surface_id); var box = wdi.graphics.getBoxFromSrcArea(base.box); context.fillStyle = "black"; context.fillRect(box.x, box.y, box.width, box.height); }, drawTransparent: function(spiceMessage) { var drawBase = spiceMessage.args.base; var surface_id = drawBase.surface_id; //calculate src_area box var box = wdi.graphics.getBoxFromSrcArea(spiceMessage.args.src_area); var dest_box = wdi.graphics.getBoxFromSrcArea(drawBase.box); //get destination iamge, in imagedata format because is what we need var destImg = this.clientGui.getContext(surface_id).getImageData(dest_box.x, dest_box.y, dest_box.width, dest_box.height); wdi.graphics.getImageFromSpice(spiceMessage.args.image.imageDescriptor, spiceMessage.args.image.data, this.clientGui, function(srcImg) { if(srcImg) { //adapt to src_area srcImg = wdi.graphics.getRect(box, srcImg); var source = wdi.graphics.getDataFromImage(srcImg).data; var dest = destImg.data; var length = source.length-1; var resultImageData = this.clientGui.getContext(surface_id).createImageData(dest_box.width, dest_box.height); var color = spiceMessage.args.transparent_true_color; while(length>0) { resultImageData.data[length] = 255; //alpha if(source[length-1] === color.b && source[length-2] === color.g && source[length-3] === color.r) { resultImageData.data[length-1] = dest[length-1]; //b resultImageData.data[length-2] = dest[length-2]; //g resultImageData.data[length-3] = dest[length-3]; //r } else { resultImageData.data[length-1] = source[length-1]; //b resultImageData.data[length-2] = source[length-2]; //g resultImageData.data[length-3] = source[length-3]; //r } length-=4; } var resultImage = wdi.graphics.getImageFromData(resultImageData); this.drawClip(resultImage, dest_box, this.clientGui.getContext(surface_id)); } else { //failed to get image, cache error? wdi.Debug.log('Unable to get image!'); } }, this); }, drawText: function(spiceMessage) { var context = this.clientGui.getContext(spiceMessage.args.base.surface_id); var bbox = spiceMessage.args.base.box; var clip = spiceMessage.args.base.clip; var text = spiceMessage.args; var string = text.glyph_string; var bpp = string.flags === 1 ? 1 : string.flags * 2; if (text.back_mode !== 0) { wdi.graphics.drawBackText(this.clientGui, context, text); } wdi.graphics.drawString(context, string, bpp, text.fore_brush, clip.type, this); }, /** * Clears all color palettes * @param spiceMessage * @param app */ invalPalettes: function(spiceMessage) { wdi.ImageCache.clearPalettes(); } });