//
// Copyright (C) 2008 Aaron Bockover, abockover@novell.com
// Copyright (C) 2008 Jordi Mas i Hernandez, jmas@softcatala.org
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Runtime.InteropServices;

using Cairo;

namespace Mistelix.Widgets
{
	public class PixbufImageSurface : DataImageSurface, IDisposable
	{
		public PixbufImageSurface (Gdk.Pixbuf pixbuf) : this (pixbuf, false)
		{
		}

		public PixbufImageSurface (Gdk.Pixbuf pixbuf, bool disposePixbuf) : this (disposePixbuf ? pixbuf : null, 
		    pixbuf.Width, pixbuf.Height, pixbuf.NChannels, pixbuf.Rowstride, pixbuf.Pixels)
		{
		}

		// This ctor is to avoid multiple queries against the GdkPixbuf for width/height
		private PixbufImageSurface (Gdk.Pixbuf pixbuf, int width, int height, int channels, int rowstride, IntPtr pixels) 
		    : this (pixbuf, Marshal.AllocHGlobal (width * height * 4), width, height, channels, rowstride, pixels)
		{
		}

		private PixbufImageSurface (Gdk.Pixbuf pixbuf, IntPtr data, int width, int height, int channels, int rowstride, IntPtr pixels) 
		    : base (data, channels == 3 ? Format.Rgb24 : Format.Argb32, width, height, width * 4)
		{
			this.data = data;

			CreateSurface (width, height, channels, rowstride, pixels);

			if (pixbuf != null && pixbuf.Handle != IntPtr.Zero) {
				pixbuf.Dispose ();
			}
		}


		private unsafe void CreateSurface (int width, int height, int channels, int gdk_rowstride, IntPtr pixels)
		{
		    byte *gdk_pixels = (byte *)pixels;
		    byte *cairo_pixels = (byte *)data;
		    
		    for (int i = height; i > 0; i--) {
			byte *p = gdk_pixels;
			byte *q = cairo_pixels;
		
			if (channels == 3) {
			    byte *end = p + 3 * width;
			    while (p < end) {
				if (is_le) {
				    q[0] = p[2];
				    q[1] = p[1];
				    q[2] = p[0];
				} else {
				    q[1] = p[0];
				    q[2] = p[1];
				    q[3] = p[2];
				}
			    
				p += 3;
				q += 4;
			    }
			} else {
			    byte *end = p + 4 * width;
			    while (p < end) {
				if (is_le) {
				    q[0] = Mult (p[2], p[3]);
				    q[1] = Mult (p[1], p[3]);
				    q[2] = Mult (p[0], p[3]);
				    q[3] = p[3];
				} else {
				    q[0] = p[3];
				    q[1] = Mult (p[0], p[3]);
				    q[2] = Mult (p[1], p[3]);
				    q[3] = Mult (p[2], p[3]);
				}
				
				p += 4;
				q += 4;
			    }
			}
		
			gdk_pixels += gdk_rowstride;
			cairo_pixels += 4 * width;
		    }
		}

		private static byte Mult (byte c, byte a)
		{
			int t = c * a + 0x7f; 
			return (byte)(((t >> 8) + t) >> 8);
		}

		public void Render (Cairo.Context cr, int x, int y)
		{
			cr.Save ();
			cr.SetSourceSurface (this, x, y);
			cr.Paint ();
			cr.Restore ();			
		}
	}
}	

