/*
 * OverLua RGB(A) image interface
 *

    Copyright 2007  Niels Martin Hansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    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 General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

    Contact:
    E-mail: <jiifurusu@gmail.com>
    IRC: jfs in #aegisub on irc.rizon.net

 */

#ifndef VIDEO_FRAME_H
#define VIDEO_FRAME_H

#include "../lua51/src/lua.h"
#include "../lua51/src/lauxlib.h"
#include "cairo_wrap.h"
#include <stddef.h>
#include <memory.h>
#include <stdint.h>
#include <omp.h>
#include <math.h>
#include <assert.h>


// Forward
class BaseImageAggregate;


// Supported pixel formats
namespace PixelFormat {
	// A constant value with a fake assignment operator, taking up no space
	template <class T, T v>
	struct NopAssigningConstant {
		operator T() const { return v; }
		void operator = (const T &n) { }
	};
	typedef NopAssigningConstant<uint8_t,255> ROA; // "read only alpha"
	typedef NopAssigningConstant<uint8_t,0> ROC; // "read only colour"

	// 24 bit formats
	struct RGB {
		uint8_t r, g, b;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline ROA A() const { return ROA(); }
		RGB() : r(0), g(0), b(0) { }
		template <class PixFmt> RGB(const PixFmt &src) { r = src.R(); g = src.G(); b = src.B(); }
	};
	struct BGR {
		uint8_t b, g, r;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline ROA A() const { return ROA(); }
		BGR() : r(0), g(0), b(0) { }
		template <class PixFmt> BGR(const PixFmt &src) { r = src.R(); g = src.G(); b = src.B(); }
	};

	// 32 bit alpha-less formats
	struct RGBX {
		uint8_t r, g, b, x;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline ROA A() const { return ROA(); }
		RGBX() : r(0), g(0), b(0) { }
		template <class PixFmt> RGBX(const PixFmt &src) { r = src.R(); g = src.G(); b = src.B(); }
	};
	struct BGRX {
		uint8_t b, g, r, x;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline ROA A() const { return ROA(); }
		BGRX() : r(0), g(0), b(0) { }
		template <class PixFmt> BGRX(const PixFmt &src) { r = src.R(); g = src.G(); b = src.B(); }
	};
	struct XRGB {
		uint8_t x, r, g, b;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline ROA A() const { return ROA(); }
		XRGB() : r(0), g(0), b(0) { }
		template <class PixFmt> XRGB(const PixFmt &src) { r = src.R(); g = src.G(); b = src.B(); }
	};
	struct XBGR {
		uint8_t x, b, g, r;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline ROA A() const { return ROA(); }
		XBGR() : r(0), g(0), b(0) { }
		template <class PixFmt> XBGR(const PixFmt &src) { r = src.R(); g = src.G(); b = src.B(); }
	};

	// 32 bit with alpha
	struct RGBA {
		uint8_t r, g, b, a;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline uint8_t &A() { return a; } inline uint8_t A() const { return a; }
		RGBA() : r(0), g(0), b(0), a(0) { }
		template <class PixFmt> RGBA(const PixFmt &src) { a = src.a; r = src.r; g = src.g; b = src.b; }
	};
	struct BGRA {
		uint8_t b, g, r, a;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline uint8_t &A() { return a; } inline uint8_t A() const { return a; }
		BGRA() : r(0), g(0), b(0), a(0) { }
		template <class PixFmt> BGRA(const PixFmt &src) { a = src.A(); r = src.R(); g = src.G(); b = src.B(); }
	};
	struct ARGB {
		uint8_t a, r, g, b;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline uint8_t &A() { return a; } inline uint8_t A() const { return a; }
		ARGB() : r(0), g(0), b(0), a(0) { }
		template <class PixFmt> ARGB(const PixFmt &src) { a = src.A(); r = src.R(); g = src.G(); b = src.B(); }
	};
	struct ABGR {
		uint8_t a, b, g, r;
		inline uint8_t &R() { return r; } inline uint8_t R() const { return r; }
		inline uint8_t &G() { return g; } inline uint8_t G() const { return g; }
		inline uint8_t &B() { return b; } inline uint8_t B() const { return b; }
		inline uint8_t &A() { return a; } inline uint8_t A() const { return a; }
		ABGR() : r(0), g(0), b(0), a(0) { }
		template <class PixFmt> ABGR(const PixFmt &src) { a = src.A(); r = src.R(); g = src.G(); b = src.B(); }
	};

	// Alpha only
	struct A8 {
		uint8_t a;
		inline ROC R() const { return ROC(); }
		inline ROC G() const { return ROC(); }
		inline ROC B() const { return ROC(); }
		inline uint8_t &A() { return a; } inline uint8_t A() const { return a; }
		A8() : a(0) { }
		template <class PixFmt> A8(const PixFmt &src) { a = src.A(); }
	};

	// cairo types
	// These are only true for little endian architectures
	// If OverLua is ever to run on big endian archs some conditional compiling should be used here
	typedef BGRX cairo_rgb24;
	typedef BGRA cairo_argb32;
};


// Support any interleaved RGB format with 8 bit per channel
// You usually don't want to instance this class directly,
// look at OverLuaFrameAggregate defined below
template<class PixFmt>
class BaseImage {
public:
	typedef BaseImage<PixFmt> MyType;

	// video properties
	int width;
	int height;
	ptrdiff_t stride;
	unsigned char *data;

	// Access a pixel value
	inline const PixFmt &Pixel(int x, int y) const
	{
		return *((PixFmt*)(data + y*stride) + x)
	}

	inline PixFmt &Pixel(int x, int y)
	{
		return *((PixFmt*)(data + y*stride) + x);
	}

	BaseImage(unsigned _width, unsigned _height, ptrdiff_t _stride, unsigned char *_data)
		: width(_width), height(_height), stride(_stride), data(_data)
	{
		owndata = false;
		// nothing further to init
	}

	BaseImage(const MyType &src, bool copydata = false)
	{
		width = src.width;
		height = src.height;
		stride = src.stride;
		owndata = copydata;
		if (copydata) {
			data = new unsigned char[height*stride];
			memcpy(data, src.data, height*stride);
		} else {
			data = src.data;
		}
	}

	~BaseImage()
	{
		if (owndata)
			delete[] data;
	}

	// should never be called more than once on the same C++ object
	void CreateLuaObject(lua_State *L, BaseImageAggregate *aggregate = 0)
	{
		// create userdata object
		MyType **ud = (MyType**)lua_newuserdata(L, sizeof(MyType*));
		*ud = this;

		// create metatable
		lua_newtable(L);
		lua_pushcclosure(L, lua_indexget, 0);
		lua_setfield(L, -2, "__index");
		lua_pushcclosure(L, lua_indexset, 0);
		lua_setfield(L, -2, "__newindex");
		lua_pushcclosure(L, lua_getpixel, 0);
		lua_setfield(L, -2, "__call");
		lua_pushcclosure(L, lua_finalize, 0);
		lua_setfield(L, -2, "__gc");
		if (aggregate) {
			lua_pushlightuserdata(L, aggregate);
			lua_setfield(L, -2, "image");
		}
		lua_setmetatable(L, -2);
	}

private:
	bool owndata;

	// set a pixel colour
	static int lua_indexset(lua_State *L)
	{
		// first arg = object
		// second arg = index
		// third arg = value
		MyType **ud = (MyType**)lua_touserdata(L, 1);

		if (lua_isnumber(L, 2)) {
			if (lua_istable(L, 3)) {
				int n = (int)lua_tointeger(L, 2);
				int x = n % (*ud)->width;
				int y = n / (*ud)->width;
				if (x < 0 || y < 0 || x >= (*ud)->width || y >= (*ud)->height) return 0;

				// read first three entries from table
				PixFmt color;
				lua_pushnil(L);
				if (!lua_next(L, 3)) goto badtable;
				if (!lua_isnumber(L, -1)) goto badtable;
				color.r = (unsigned char)lua_tointeger(L, -1);
				lua_pop(L, 1);
				if (!lua_next(L, 3)) goto badtable;
				if (!lua_isnumber(L, -1)) goto badtable;
				color.g = (unsigned char)lua_tointeger(L, -1);
				lua_pop(L, 1);
				if (!lua_next(L, 3)) goto badtable;
				if (!lua_isnumber(L, -1)) goto badtable;
				color.b = (unsigned char)lua_tointeger(L, -1);
				lua_pop(L, 2);

				(*ud)->Pixel(x, y) = color;
				return 0;

			} else {
badtable:
				lua_pushliteral(L, "Value set into image pixel must be a table with 3 entries");
				lua_error(L);
				return 0;
			}
		}

		lua_pushliteral(L, "Undefined field in image");
		lua_error(L);
		return 0;
	}

	// get a pixel colour or some other property
	static int lua_indexget(lua_State *L)
	{
		// first arg = object
		// second arg = index
		MyType **ud = (MyType**)lua_touserdata(L, 1);

		if (lua_type(L, 2) == LUA_TSTRING) {
			const char *fieldname = lua_tostring(L, 2);
			if (strcmp(fieldname, "width") == 0) {
				lua_pushnumber(L, (*ud)->width);
			} else if (strcmp(fieldname, "height") == 0) {
				lua_pushnumber(L, (*ud)->height);
			} else if (strcmp(fieldname, "copy") == 0) {
				lua_pushvalue(L, 1);
				lua_pushcclosure(L, lua_copyfunc, 1);
			} else if (strcmp(fieldname, "create_cairo_surface") == 0) {
				lua_pushvalue(L, 1);
				lua_pushcclosure(L, lua_create_cairo_surface, 1);
			} else if (strcmp(fieldname, "overlay_cairo_surface") == 0) {
				lua_pushvalue(L, 1);
				lua_pushcclosure(L, lua_overlay_cairo_surface, 1);
			} else {
				lua_pushfstring(L, "Undefined field name in image: %s", fieldname);
				lua_error(L);
			}

			return 1;

		}

		lua_pushfstring(L, "Unhandled field type indexing image: %s", lua_typename(L, lua_type(L, 2)));
		lua_error(L);
		return 0;
	}

	static int lua_getpixel(lua_State *L)
	{
		// first arg = object
		// second arg = x
		// third arg = y
		MyType **ud = (MyType**)lua_touserdata(L, 1);

		int x = luaL_checkint(L, 2);
		int y = luaL_checkint(L, 3);

		if (x < 0 || y < 0 || x >= (*ud)->width || y >= (*ud)->height) {
			lua_pushinteger(L, 0);
			lua_pushinteger(L, 0);
			lua_pushinteger(L, 0);
		} else {
			const PixFmt &p = (*ud)->Pixel(x, y);
			lua_pushinteger(L, p.r);
			lua_pushinteger(L, p.g);
			lua_pushinteger(L, p.b);
		}
		return 3;
	}

	static int lua_finalize(lua_State *L)
	{
		MyType **ud = (MyType**)lua_touserdata(L, 1);
		delete *ud;
		return 0;
	}

	static int lua_copyfunc(lua_State *L)
	{
		MyType **ud = (MyType**)lua_touserdata(L, lua_upvalueindex(1));
		MyType *copy = new MyType(**ud, true);
		copy->CreateLuaObject(L);
		return 1;
	}

	static int lua_create_cairo_surface(lua_State *L)
	{
		MyType **ud = (MyType**)lua_touserdata(L, lua_upvalueindex(1));

		// Create a new surface of same resolution
		// Always RGB24 format since we don't support video with alpha
		cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_RGB24, (*ud)->width, (*ud)->height);
		if (!surf || cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
			lua_pushliteral(L, "Unable to create cairo surface from image");
			lua_error(L);
			return 0;
		}

		// Prepare copying pixels from frame to surface
		unsigned char *surfdata = cairo_image_surface_get_data(surf);
		int surfstride = cairo_image_surface_get_stride(surf);
		
		// Copy pixels
		int height = (*ud)->height;
		int width = (*ud)->width;
#pragma omp parallel for
		for (int y = 0; y < height; y++) {
			PixFmt *ipix = (PixFmt*)((*ud)->data + y*((*ud)->stride));
			PixelFormat::cairo_rgb24 *opix = (PixelFormat::cairo_rgb24*)(surfdata + y*surfstride);
			for (int x = 0; x < width; x++) {
				*opix++ = *ipix++;
			}
		}
		cairo_surface_mark_dirty(surf);

		// Create Lua object
		LuaCairoSurface *lsurf = new LuaCairoSurface(L, surf);

		// Release our reference to the surface
		cairo_surface_destroy(surf);

		// Return it to Lua
		return 1;
	}

	static int lua_overlay_cairo_surface(lua_State *L)
	{
		// Get inputs
		MyType **ud = (MyType**)lua_touserdata(L, lua_upvalueindex(1));
		LuaCairoSurface *surfwrap = LuaCairoSurface::GetObjPointer(L, 1);
		if (!surfwrap) {
			lua_pushliteral(L, "Argument to overlay_cairo_surface is not a cairo surface");
			lua_error(L);
			return 0;
		}
		int xpos = luaL_checkint(L, 2);
		int ypos = luaL_checkint(L, 3);

		// More argument checks
		cairo_surface_t *surf = surfwrap->GetSurface();
		if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_IMAGE) {
			lua_pushliteral(L, "Argument to overlay_cairo_surface is not a cairo image surface");
			lua_error(L);
			return 0;
		}
		
		// Prepare some data
		int fwidth = (*ud)->width, fheight = (*ud)->height;
		int swidth = cairo_image_surface_get_width(surf), sheight = cairo_image_surface_get_height(surf);
		int sstride = cairo_image_surface_get_stride(surf);
		// Prepare and get read pointer for source image
		cairo_surface_flush(surf);
		unsigned char *sdata = cairo_image_surface_get_data(surf);

		// Check that the overlaid surface won't be entirely outside the frame
		if (xpos <= -swidth || ypos <= -sheight || xpos > fwidth || ypos > fheight)
			return 0;

		// Pick overlay algorithm
		cairo_format_t sformat = cairo_image_surface_get_format(surf);

		if (sformat == CAIRO_FORMAT_ARGB32) {
			// This has pre-multipled alpha
			int fy = ypos; // frame y
			if (fy < 0) fy = 0, sdata -= ypos, sheight -= ypos;
			int slines_to_compose = sheight, flines_to_compose = fheight;
			int lines_to_compose = (slines_to_compose<flines_to_compose)?slines_to_compose:flines_to_compose;
#pragma omp parallel for
			for (int composition_line = 0; composition_line < lines_to_compose; composition_line++) {
				PixelFormat::cairo_argb32 *sline = (PixelFormat::cairo_argb32*)(sdata + composition_line*sstride);
				int fx = xpos;
				int sx = 0;
				if (fx < 0) fx = 0, sx = -xpos;
				for ( ; sx < swidth && fx < fwidth; fx++, sx++) {
					PixFmt &pix = (*ud)->Pixel(fx, fy+composition_line);
					unsigned char a = 0xff - sline[sx].a;
					pix.r = sline[sx].r + a*pix.r/255;
					pix.g = sline[sx].g + a*pix.g/255;
					pix.b = sline[sx].b + a*pix.b/255;
				}
			}
		}

		else if (sformat == CAIRO_FORMAT_RGB24) {
			// Assume opaque alpha for all pixels
			int fy = ypos; // frame y
			if (fy < 0) fy = 0, sdata -= ypos, sheight -= ypos;
			int slines_to_compose = sheight, flines_to_compose = fheight;
			int lines_to_compose = (slines_to_compose<flines_to_compose)?slines_to_compose:flines_to_compose;
#pragma omp parallel for
			for (int composition_line = 0; composition_line < lines_to_compose; composition_line++) {
				PixelFormat::cairo_rgb24 *sline = (PixelFormat::cairo_rgb24*)(sdata + composition_line*sstride);
				int fx = xpos;
				int sx = 0;
				if (fx < 0) fx = 0, sx = -xpos;
				for ( ; sx < swidth && fx < fwidth; fx++, sx++) {
					(*ud)->Pixel(fx, fy+composition_line) = sline[sx];
				}
			}
		}

		else if (sformat == CAIRO_FORMAT_A8) {
			// This one is problematic - it doesn't contain any colour information
			// Two primary choices would be to fill with either black or white,
			// but neither would be an optimum solution.
			// A third option would be to take a fourth argument to this function,
			// specifying the colour to be used.
			lua_pushliteral(L, "8 bpp alpha images are not (yet) supported for overlay operations");
			lua_error(L);
			return 0;
		}

		else if (sformat == CAIRO_FORMAT_A1) {
			lua_pushliteral(L, "1 bpp alpha images are not supported for overlay operations");
			lua_error(L);
			return 0;
		}

		else {
			lua_pushliteral(L, "Unknown source image format for overlay operation");
			lua_error(L);
			return 0;
		}

		return 0;
	}

};


// Now something so we can pretend all images have the same pixel format
// and can pass around pointers to objects of one fixed base class.
class BaseImageAggregate {
public:
	virtual PixelFormat::ARGB GetPixel(int x, int y) = 0;
	virtual void SetPixel(int x, int y, const PixelFormat::ARGB &val) = 0;
	virtual unsigned GetWidth() = 0;
	virtual unsigned GetHeight() = 0;
	virtual void CreateLuaObject(lua_State *L) = 0;
};

template <class PixFmt>
class BaseImageAggregateImpl : public BaseImageAggregate {
public:
	typedef BaseImage<PixFmt> ImageType;

private:
	ImageType *frame;
	bool ownframe;

public:
	BaseImageAggregateImpl(unsigned _width, unsigned _height, ptrdiff_t _stride, unsigned char *_data)
	{
		frame = new ImageType(_width, _height, _stride, _data);
		ownframe = true;
	}

	BaseImageAggregateImpl(ImageType *_frame)
	{
		frame = _frame;
		ownframe = false;
	}

	PixelFormat::ARGB GetPixel(int x, int y)
	{
		return PixelFormat::ARGB(frame->Pixel(x, y));
	}

	void SetPixel(int x, int y, const PixelFormat::ARGB &val)
	{
		frame->Pixel(x, y) = PixFmt(val);
	}

	unsigned GetWidth()
	{
		return frame->width;
	}

	unsigned GetHeight()
	{
		return frame->height;
	}

	void CreateLuaObject(lua_State *L)
	{
		frame->CreateLuaObject(L, this);
	}

};

// All common, sensible formats
typedef BaseImageAggregateImpl<PixelFormat::RGB> ImageRGB;
typedef BaseImageAggregateImpl<PixelFormat::BGR> ImageBGR;
typedef BaseImageAggregateImpl<PixelFormat::RGBX> ImageRGBX;
typedef BaseImageAggregateImpl<PixelFormat::BGRX> ImageBGRX;
typedef BaseImageAggregateImpl<PixelFormat::XRGB> ImageXRGB;
typedef BaseImageAggregateImpl<PixelFormat::XBGR> ImageXBGR;
typedef BaseImageAggregateImpl<PixelFormat::RGBA> ImageRGBA;
typedef BaseImageAggregateImpl<PixelFormat::BGRA> ImageBGRA;
typedef BaseImageAggregateImpl<PixelFormat::ARGB> ImageARGB;
typedef BaseImageAggregateImpl<PixelFormat::ABGR> ImageABGR;


// Access pixels with various edge conditions
namespace EdgeCondition {
	template<class PixFmt>
	struct Blackness {
		static inline PixFmt &get(BaseImage<PixFmt> &img, int x, int y)
		{
			if (x < 0 || y < 0 || x >= img.width || x >= img.height) {
				return PixFmt(); // all construct with all zeroes
			} else {
				return img.Pixel(x,y);
			}
		}
	};

	template<class PixFmt>
	struct Closest {
		static inline PixFmt &get(BaseImage<PixFmt> &img, int x, int y)
		{
			if (x < 0) x = 0;
			if (y < 0) y = 0;
			if (x >= img.width) x = img.width-1;
			if (y >= img.height) y = img.height - 1;
			return img.Pixel(x,y);
		}
	};

	template<class PixFmt>
	struct Repeat {
		static inline PixFmt &get(BaseImage<PixFmt> &img, int x, int y)
		{
			while (x < 0) x += img.width;
			while (y < 0) y += img.height;
			while (x >= img.width) x -= img.width;
			while (y >= img.height) y -= img.height;
			return img.Pixel(x,y);
		}
	};

	template<class PixFmt>
	struct Mirror {
		static inline PixFmt &get(BaseImage<PixFmt> &img, int x, int y)
		{
			while (x < 0) x += img.width*2;
			while (y < 0) y += img.height*2;
			while (x >= img.width*2) x -= img.width*2;
			while (y >= img.height*2) y -= img.height*2;
			if (x >= img.width) x = img.width*2 - x;
			if (y >= img.height) y = img.height*2 - y;
			return img.Pixel(x,y);
		}
	};
}


// FIXME: this is completely broken, the compiler won't accept it
// when instantiated with one of the EdgeCondition functions for EdgeCond
template<class PixFmt, typename EdgeCond>
inline PixFmt GetPixelBilinear(BaseImage<PixFmt> &img, double x, double y)
{
	PixFmt res;
	double xpct = x - floor(x), ypct = y - floor(y);

	if (xpct == 0 && ypct == 0)
		return EdgeCond::get(img, (int)x, (int)y);

	const PixFmt &src11 = EdgeCond::get(img, (int)x, (int)y);
	const PixFmt &src12 = EdgeCond::get(img, (int)x, 1+(int)y);
	const PixFmt &src21 = EdgeCond::get(img, 1+(int)x, (int)y);
	const PixFmt &src22 = EdgeCond::get(img, 1+(int)x, 1+(int)y);

	res.R() = (uint8_t)((1-xpct) * (1-ypct) * src11.R() + (1-xpct) * ypct * src12.R() + xpct * (1-ypct) * src21.R() + xpct * ypct * src22.R());
	res.G() = (uint8_t)((1-xpct) * (1-ypct) * src11.G() + (1-xpct) * ypct * src12.G() + xpct * (1-ypct) * src21.G() + xpct * ypct * src22.G());
	res.B() = (uint8_t)((1-xpct) * (1-ypct) * src11.B() + (1-xpct) * ypct * src12.B() + xpct * (1-ypct) * src21.B() + xpct * ypct * src22.B());
	res.A() = (uint8_t)((1-xpct) * (1-ypct) * src11.A() + (1-xpct) * ypct * src12.A() + xpct * (1-ypct) * src21.A() + xpct * ypct * src22.A());

	return res;
}


#endif