/* Copyright 2017-present Renesas Electronics Corporation and other contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <string>
#include <sstream>
#include <algorithm>

extern "C" {
	//#include <unistd.h>
	int close(int fd);
	ssize_t read(int fd, void* buf, size_t count);
	ssize_t write(int fd, const void* buf, size_t count);
	off_t lseek(int fd, off_t offset, int whence);
	int unlink(const char* pathname);
}	// extern "C"

#include <mbed.h>
#include "posix4mbed.h"
#include "peripheral_io_4mbed.h"
#include "primitives.h"

#include "DisplayBace.h"
#include "JPEG_Converter.h"
#include "dcache-control.h"
#include "AsciiFont.h"

using namespace os::primitives;

namespace os { namespace primitives {

	CondVar::CondVar() : mutex_(), sems_() {
	}

	void CondVar::notify_one() {
		mutex_.lock();
		if( !sems_.empty() ) {
			sems_.front()->release();
			sems_.pop_front();
		}
		mutex_.unlock();
	}

	void CondVar::wait( P4(pthread_mutex_t)* lock ) {
		Semaphore sem;
		{
			mutex_.lock();
			sems_.push_back( &sem );
			mutex_.unlock();
		}
		{
			P4(pthread_mutex_unlock)( lock );
			sem.wait();
			P4(pthread_mutex_lock)( lock );
		}
	}

	void SocketAddress::dbg() const {
		printf( "SocketAddress [%s:%d] \n", get_ip_address(), get_port() );
	}

	SocketAddress SocketAddress::trans(const P4(sockaddr)& addr) {
		helper::sockaddrs addrs;
		addrs.csa = &addr;
		if( addr.sa_family == AF_INET ) {
			const P4(sockaddr_in)& sa4 = *addrs.csa4;
			return SocketAddress( &sa4.sin_addr, NSAPI_IPv4, helper::ntohs( sa4.sin_port ) );
		}
		else if( addr.sa_family == AF_INET6 ) {
			const P4(sockaddr_in6)& sa6 = *addrs.csa6;
			return SocketAddress( &sa6.sin6_addr, NSAPI_IPv6, helper::ntohs( sa6.sin6_port ) );
		}
		TRACE(INFO "SocketAddress::trans unknown sa_family = %d", addr.sa_family);
		return SocketAddress();
	}

	int SocketAddress::trans(const SocketAddress& sa, void* addr, P4(socklen_t)* addrlen) {
		if( sa.get_ip_version() == NSAPI_IPv4 ) {
			P4(sockaddr_in) sai = {0};
			sai.sin_len = sizeof( sai );
			sai.sin_family = AF_INET;
			sai.sin_port = helper::htons( sa.get_port() );
				union { uint8_t* bytes; P4(in_addr)* addr; } cnv;
				cnv.bytes = sa.get_addr().bytes;
			sai.sin_addr = *cnv.addr;
			memcpy( addr, &sai, *addrlen < sizeof( sai ) ? *addrlen : sizeof( sai ) );
			*addrlen = sizeof( sai );
			return helper::clear_errno();
		}
		else if( sa.get_ip_version() == NSAPI_IPv6 ) {
			P4(sockaddr_in6) sai = {0};
			sai.sin6_len = sizeof( sai );
			sai.sin6_family = AF_INET6;
			sai.sin6_port = helper::htons( sa.get_port() );
		//	sai.sin6_flowinfo = 0;
				union { uint8_t* bytes; P4(in6_addr)* addr; } cnv;
				cnv.bytes = sa.get_addr().bytes;
			sai.sin6_addr = *cnv.addr;
		//	sai.sin6_scope_id = 0;
			memcpy( addr, &sai, *addrlen < sizeof( sai ) ? *addrlen : sizeof( sai ) );
			*addrlen = sizeof( sai );
			return helper::clear_errno();
		}
		return helper::set_errno( EINVAL );
	}

	bool Nic::connect() {
		return is_ip_assigned() || (eth_.connect() == 0);
	}

	bool Nic::disconnect() {
		return !is_ip_assigned() || (eth_.disconnect() == 0);
	}

	bool Nic::is_ip_assigned() const {
		return eth_.get_ip_address();
	}

	Nic::Params Nic::parse_params( const std::string& str_params ) const {
		Params params;
		std::istringstream iss( str_params );
		while( iss ) {
			std::string key, value;
			iss >> key >> value;
			params[ key ] = value;
		}
		return params;
	}

	/*static*/
	char* Nic::enumerate() {
	#define NIC(name,tp) "\""#name"\":{\"type\":\""#tp"\"}"
	#define NIC_AND ","
		const char* nics = "{"
			NIC(ETHERNET, eth)
//			NIC_AND NIC(WIFI_ESP8266, wifi)
//			NIC_AND NIC(WIFI_BP3595, wifi)
		"}";
	#undef NIC_AND
	#undef NIC
		return strdup( nics );
	}

	bool Nic::ifup(const std::string& nic, const std::string& str_params) {
		if( nic != targetIF() || is_ip_assigned() ) {
			return false;
		}
		Params params = parse_params( str_params );
		bool dhcp = ( params[ "dhcp:"] == "1" );
		if( eth_.set_dhcp( dhcp ) < 0 ) {
			return false;
		}
		std::string& ip   = params[ "ip:" ];
		std::string& mask = params[ "netmask:" ];
		std::string& gw   = params[ "gateway:" ];
		if( !dhcp && eth_.set_network( ip.c_str(), mask.c_str(), gw.c_str() ) < 0 ) {
			return false;
		}
		std::string& dns = params[ "dns:" ];
		if( !dns.empty() && eth_.add_dns_server( SocketAddress( dns.c_str() ) ) < 0 ) {
			return false;
		}
		return connect();
	}

	bool Nic::ifdown(const std::string& nic) {
		if( nic != targetIF() || !is_ip_assigned() ) {
			return false;
		}
		return disconnect();
	}

	char* Nic::ifconfig(const std::string& nic) {
		if( nic != targetIF() ) {
			return strdup( "" );
		}
		std::string conf;
	#define JSON_BEGIN_KV(str,key,value) str+=std::string("{\""#key"\":\"") + value + "\""
	#define JSON_NEXT_KV(str,key,value)  str+=std::string(",\""#key"\":\"") + value + "\""
	#define JSON_END(str)                str+=std::string("}")
		JSON_BEGIN_KV( conf, NIC, targetIF() );
		if( !is_ip_assigned() ) {
			JSON_NEXT_KV( conf, inet, "none" );
		}
		else {
			JSON_NEXT_KV( conf, HWaddr, eth_.get_mac_address() );
			JSON_NEXT_KV( conf, inet, eth_.get_ip_address() );
			JSON_NEXT_KV( conf, netmask, eth_.get_netmask() );
			JSON_NEXT_KV( conf, gateway, eth_.get_gateway() );
		}
		JSON_END( conf );
	#undef JSON_END
	#undef JSON_NEXT_KV
	#undef JSON_BEGIN_KV
		return strdup( conf.c_str() );
	}

	bool Nic::ntpdate(const std::string& nic, const char* server/*="0.pool.ntp.org"*/, int port/*=123*/, int timeout/*=10*1000*/) {
		if( nic != targetIF() || !is_ip_assigned() ) {
			return false;
		}

		UDPSocket sock;
		sock.open( &eth_ );
		sock.set_timeout( timeout );

		uint32_t send[12] = {0x1B, 0};
	    if( sizeof( send ) != sock.sendto( server, port, send, sizeof( send ) ) ) {
			return false;
		}

		SocketAddress src;
		uint32_t recv[12] = {0};
	    if( sizeof( recv ) != sock.recvfrom( &src, recv, sizeof( recv ) ) ) {
			return false;
		}

		const uint32_t time1970 = 25567u * 24 * 60 * 60;
		const uint32_t time1900 = helper::ntohl( recv[ 10 ] );
		set_time( time1900 - time1970 );
		return true;
	}

	DisplayBase display;
	bool isLcdInitialized = false;				// LCD初期化フラグ

}}	// namespace os::primitives::

namespace os { namespace video {

	bool isStarted = false;				// カメラ動作中フラグ

	bool Video::camera_initialize() {
		/* OV7725 camera input config */
		const char OV7725_InitRegTable[][2] = {
			{0x0D, 0x41}, /* COM4 */
			{0x0F, 0xC5}, /* COM6 */
			{0x11, 0x00}, /* CLKRC */
			{0x14, 0x1F}, /* COM9 Drop VSYNC/HREF */
			{0x15, 0x40}, /* COM10 HSYNC*/
			{0x17, 0x22}, /* HSTART */
			{0x18, 0xA4}, /* HSIZE */
			{0x19, 0x07}, /* VSTRT */
			{0x1A, 0xF0}, /* VSIZE */
			{0x22, 0x99}, /* BDBase */
			{0x23, 0x02}, /* BDMStep */
			{0x24, 0x60}, /* AEW */
			{0x25, 0x50}, /* AEB */
			{0x26, 0xA1}, /* VPT */
			{0x29, 0xA0}, /* HOutSize */
			{0x2A, 0x00}, /* EXHCH */
			{0x2B, 0x00}, /* EXHCL */
			{0x2C, 0xF0}, /* VOutSize */
			{0x32, 0x00}, /* HREF */
			{0x33, 0x01}, /* DM_LNL */
			{0x3D, 0x03}, /* COM12 */
			{0x42, 0x7F}, /* TGT_B */
			{0x4D, 0x09}, /* FixGain */
			{0x63, 0xE0}, /* AWB_Ctrl0 */
			{0x64, 0xFF}, /* DSP_Ctrl1 */
			{0x65, 0x20}, /* DSP_Ctrl2 */
			{0x66, 0x00}, /* DSP_Ctrl3 */
			{0x67, 0x48}, /* DSP_Ctrl4 */
			{0x6B, 0xAA}, /* AWBCtrl3 */
			{0x7E, 0x04}, /* GAM1 */
			{0x7F, 0x0E}, /* GAM2 */
			{0x80, 0x20}, /* GAM3 */
			{0x81, 0x43}, /* GAM4 */
			{0x82, 0x53}, /* GAM5 */
			{0x83, 0x61}, /* GAM6 */
			{0x84, 0x6D}, /* GAM7 */
			{0x85, 0x76}, /* GAM8 */
			{0x86, 0x7E}, /* GAM9 */
			{0x87, 0x86}, /* GAM10 */
			{0x88, 0x94}, /* GAM11 */
			{0x89, 0xA1}, /* GAM12 */
			{0x8A, 0xBA}, /* GAM13 */
			{0x8B, 0xCF}, /* GAM14 */
			{0x8C, 0xE3}, /* GAM15 */
			{0x8D, 0x26}, /* SLOP */
			{0x90, 0x05}, /* EDGE1 */
			{0x91, 0x01}, /* DNSOff */
			{0x92, 0x05}, /* EDGE2 */
			{0x93, 0x00}, /* EDGE3 */
			{0x94, 0x80}, /* MTX1 */
			{0x95, 0x7B}, /* MTX2 */
			{0x96, 0x06}, /* MTX3 */
			{0x97, 0x1E}, /* MTX4 */
			{0x98, 0x69}, /* MTX5 */
			{0x99, 0x86}, /* MTX6 */
			{0x9A, 0x1E}, /* MTX_Ctrl */
			{0x9B, 0x00}, /* BRIGHT */
			{0x9C, 0x20}, /* CNST */
			{0x9E, 0x81}, /* UVADJ0 */
			{0xA6, 0x04}, /* SDE */
		};
		const char sw_reset_cmd[2] = {0x12, 0x80};
		int ret;
		I2C mI2c_(I2C_SDA, I2C_SCL);
		mI2c_.frequency(150000);

		mI2c_.write(0x42, sw_reset_cmd, 2);
		Thread::wait(1);

		for (uint32_t i = 0; i < (sizeof(OV7725_InitRegTable) / 2) ; i++) {
			ret = mI2c_.write(0x42, OV7725_InitRegTable[i], 2);
			if (ret != 0) {
				return false;
			}
		}

		return true;
	}

	DisplayBase::video_format_t convert_video_format(video_pixel_format_t format) {
		switch(format) {
		case VIDEO_PIXELFORMAT_YCBCR422:	return DisplayBase::VIDEO_FORMAT_YCBCR422;
		case VIDEO_PIXELFORMAT_RGB565:		return DisplayBase::VIDEO_FORMAT_RGB565;
		case VIDEO_PIXELFORMAT_RGB888:		return DisplayBase::VIDEO_FORMAT_RGB888;
		default:							return DisplayBase::VIDEO_FORMAT_YCBCR422;
		}
	}

	DisplayBase::wr_rd_swa_t get_swap_setting(video_pixel_format_t format) {
		switch(format) {
		case VIDEO_PIXELFORMAT_YCBCR422:	return DisplayBase::WR_RD_WRSWA_32_16BIT;
		case VIDEO_PIXELFORMAT_RGB565:		return DisplayBase::WR_RD_WRSWA_32_16BIT;
		case VIDEO_PIXELFORMAT_RGB888:		return DisplayBase::WR_RD_WRSWA_32BIT;
		default:							return DisplayBase::WR_RD_WRSWA_NON;
		}
	}

	bool Video::open(video_source_t video_source) {
		DisplayBase::graphics_error_t error;
		DisplayBase::video_ext_in_config_t ext_in_config;

		if(!os::primitives::isLcdInitialized) {
			error = os::primitives::display.Graphics_init(NULL);
			if(error != DisplayBase::GRAPHICS_OK) {
				return false;
			}
//			os::primitives::isLcdInitialized = true;
		}

		PinName cmos_camera_pin[11] = {
			/* data pin */
			P2_7, P2_6, P2_5, P2_4, P2_3, P2_2, P2_1, P2_0,
			/* control pin */
			P10_0,		/* DV0_CLK	 */
			P1_0,		/* DV0_Vsync */
			P1_1		/* DV0_Hsync */
		};
		DigitalOut pwdn(P3_15);
		DigitalOut rstb(P3_14);
		pwdn = 0;
		rstb = 0;
		Thread::wait(10 + 1);
		rstb = 1;
		Thread::wait(1 + 1);

		/* camera input port setting */
		error = os::primitives::display.Graphics_Dvinput_Port_Init(cmos_camera_pin, 11);
		if( error != DisplayBase::GRAPHICS_OK ) {
			return false;
		}

		ext_in_config.inp_format	 = DisplayBase::VIDEO_EXTIN_FORMAT_BT601; /* BT601 8bit YCbCr format */
		ext_in_config.inp_pxd_edge	 = DisplayBase::EDGE_RISING;			  /* Clock edge select for capturing data		   */
		ext_in_config.inp_vs_edge	 = DisplayBase::EDGE_RISING;			  /* Clock edge select for capturing Vsync signals */
		ext_in_config.inp_hs_edge	 = DisplayBase::EDGE_RISING;			  /* Clock edge select for capturing Hsync signals */
		ext_in_config.inp_endian_on	 = DisplayBase::OFF;					  /* External input bit endian change on/off	   */
		ext_in_config.inp_swap_on	 = DisplayBase::OFF;					  /* External input B/R signal swap on/off		   */
		ext_in_config.inp_vs_inv	 = DisplayBase::SIG_POL_NOT_INVERTED;	  /* External input DV_VSYNC inversion control	   */
		ext_in_config.inp_hs_inv	 = DisplayBase::SIG_POL_NOT_INVERTED;	  /* External input DV_HSYNC inversion control	   */
		ext_in_config.inp_f525_625	 = DisplayBase::EXTIN_LINE_525;			  /* Number of lines for BT.656 external input */
		ext_in_config.inp_h_pos		 = DisplayBase::EXTIN_H_POS_YCBYCR;		  /* Y/Cb/Y/Cr data string start timing to Hsync reference */
		ext_in_config.cap_vs_pos	 = 4+21;								  /* Capture start position from Vsync */
		ext_in_config.cap_hs_pos	 = 68;									  /* Capture start position form Hsync */
		ext_in_config.cap_width		 = 640;									  /* Capture width Max */
		ext_in_config.cap_height	 = 480;									  /* Capture height Max */

		if(!camera_initialize()) {
			return false;
		}
		error = os::primitives::display.Graphics_Video_init(DisplayBase::INPUT_SEL_EXT, &ext_in_config);
		if( error != DisplayBase::GRAPHICS_OK ) {
			return false;
		}

		this->video_source = video_source;

		return true;
	}

	bool Video::close(void) {
		bool ret = true;

		if(isStarted) {
			ret = stop();
		}

		return ret;
	}

	bool Video::start(void* buf) {
		DisplayBase::graphics_error_t error;
		// Video capture setting (progressive form fixed)
		os::primitives::display.Video_Write_Setting(
			DisplayBase::VIDEO_INPUT_CHANNEL_0,
			DisplayBase::COL_SYS_NTSC_358,
			buf,
			this->video_source.width * this->video_source.pixel_bytes,
			convert_video_format(this->video_source.pixel_format),
			get_swap_setting(this->video_source.pixel_format),
			this->video_source.height,
			this->video_source.width
		);

		/* Video write process start */
		error = os::primitives::display.Video_Start(DisplayBase::VIDEO_INPUT_CHANNEL_0);
		if (error != DisplayBase::GRAPHICS_OK) {
			return false;
		}

		/* Video write process stop */
		error = os::primitives::display.Video_Stop(DisplayBase::VIDEO_INPUT_CHANNEL_0);
		if (error != DisplayBase::GRAPHICS_OK) {
			return false;
		}

		/* Video write process start */
		error = os::primitives::display.Video_Start(DisplayBase::VIDEO_INPUT_CHANNEL_0);
		if (error != DisplayBase::GRAPHICS_OK) {
			return false;
		}
		isStarted = true;
		return true;
	}

	bool Video::stop() {
		DisplayBase::graphics_error_t error;
		/* Video write process stop */
		error = os::primitives::display.Video_Stop(DisplayBase::VIDEO_INPUT_CHANNEL_0);
		if (error != DisplayBase::GRAPHICS_OK) {
			return false;
		}
		isStarted = false;
		return true;
	}

}}	// namespace os::video::

namespace os { namespace lcd {

	std::list<lcd_layer_id_t> using_layer;	// 使用中レイヤー

	DisplayBase::graphics_format_t convert_graphics_format(display_pixel_format_t format) {
		switch(format) {
		case DISPLAY_PIXELFORMAT_YCBCR422:	return DisplayBase::GRAPHICS_FORMAT_YCBCR422;
		case DISPLAY_PIXELFORMAT_RGB565:	return DisplayBase::GRAPHICS_FORMAT_RGB565;
		case DISPLAY_PIXELFORMAT_RGB888:	return DisplayBase::GRAPHICS_FORMAT_RGB888;
		case DISPLAY_PIXELFORMAT_ARGB8888:	return DisplayBase::GRAPHICS_FORMAT_ARGB8888;
		case DISPLAY_PIXELFORMAT_ARGB4444:	return DisplayBase::GRAPHICS_FORMAT_ARGB4444;
		default:							return DisplayBase::GRAPHICS_FORMAT_YCBCR422;
		}
	}

	DisplayBase::graphics_layer_t convert_layer_id(lcd_layer_id_t id) {
		switch(id) {
		case LCDLAYER_0:	return DisplayBase::GRAPHICS_LAYER_0;
		case LCDLAYER_1:	return DisplayBase::GRAPHICS_LAYER_1;
		case LCDLAYER_2:	return DisplayBase::GRAPHICS_LAYER_2;
		case LCDLAYER_3:	return DisplayBase::GRAPHICS_LAYER_3;
		default:			return DisplayBase::GRAPHICS_LAYER_0;
		}
	}

	DisplayBase::wr_rd_swa_t get_swap_setting(display_pixel_format_t format) {
		switch(format) {
		case DISPLAY_PIXELFORMAT_YCBCR422:	return DisplayBase::WR_RD_WRSWA_32_16BIT;
		case DISPLAY_PIXELFORMAT_RGB565:	return DisplayBase::WR_RD_WRSWA_32_16BIT;
		case DISPLAY_PIXELFORMAT_RGB888:	return DisplayBase::WR_RD_WRSWA_32BIT;
		case DISPLAY_PIXELFORMAT_ARGB8888:	return DisplayBase::WR_RD_WRSWA_32BIT;
		case DISPLAY_PIXELFORMAT_ARGB4444:	return DisplayBase::WR_RD_WRSWA_32_16BIT;
		default:							return DisplayBase::WR_RD_WRSWA_NON;
		}
	}

	bool Lcd::open(lcd_t* lcd) {
		DisplayBase::graphics_error_t err;
		DisplayBase::lcd_config_t lcd_config;
		switch(lcd->type) {
		case LCDTYPE_4_3INCH:
			PinName lvds_pin[8] = {
				/* data pin */
				P5_7, P5_6, P5_5, P5_4, P5_3, P5_2, P5_1, P5_0
			};
			lcd->width = 480u;
			lcd->height = 272u;

			lcd_config = {
				/* GR_PEACH_4_3INCH_SHIELD Config */
				DisplayBase::LCD_TYPE_LVDS						  /* lcd_type			  */
				, (66.67)										  /* intputClock		  */
				, (13.40f)										  /* outputClock		  */
				, DisplayBase::LCD_OUTFORMAT_RGB888				  /* lcd_outformat		  */
				, DisplayBase::EDGE_RISING						  /* lcd_edge			  */
				, ((lcd->width)	 + (52u) + (43u) + (41u))		  /* h_toatal_period	  */
				, ((lcd->height) + (2u) + (12u) + (10u))		  /* v_toatal_period	  */
				, (lcd->width)									  /* h_disp_widht		  */
				, (lcd->height)									  /* v_disp_widht		  */
				, (43u)											  /* h_back_porch		  */
				, (12u)											  /* v_back_porch		  */
				, DisplayBase::LCD_TCON_PIN_2					  /* h_sync_port		  */
				, DisplayBase::SIG_POL_NOT_INVERTED				  /* h_sync_port_polarity */
				, (41u)											  /* h_sync_width		  */
				, DisplayBase::LCD_TCON_PIN_0					  /* v_sync_port		  */
				, DisplayBase::SIG_POL_NOT_INVERTED				  /* v_sync_port_polarity */
				, (10u)											  /* v_sync_width		  */
				, DisplayBase::LCD_TCON_PIN_3					  /* de_port			  */
				, DisplayBase::SIG_POL_NOT_INVERTED				  /* de_port_polarity	  */
			};

			DigitalOut lcd_pwon(P7_15, 0);
			DigitalOut lcd_blon(P8_1, 0);
			Thread::wait(100);
			lcd_pwon = 1;
			lcd_blon = 1;

			err = os::primitives::display.Graphics_Lvds_Port_Init(lvds_pin, 8);
			if(err != DisplayBase::GRAPHICS_OK) {
				return false;
			}
		}

		if(!os::primitives::isLcdInitialized) {
			err = os::primitives::display.Graphics_init((const DisplayBase::lcd_config_t*)&lcd_config);
			if(err != DisplayBase::GRAPHICS_OK) {
				return false;
			}
			os::primitives::isLcdInitialized = true;
		}

		this->lcd = *lcd;

		return true;
	}

	bool Lcd::start(lcd_layer_t lcd_layer) {
		if( using_layer.end() != std::find( using_layer.begin(), using_layer.end(), lcd_layer.id ) ) {
			return false;
		}

		DisplayBase::graphics_error_t err;
		DisplayBase::graphics_layer_t layer_id = convert_layer_id(lcd_layer.id);
		DisplayBase::rect_t rect;
		rect.hs = 0;
		rect.vs = 0;
		rect.hw = this->lcd.width;
		rect.vw = this->lcd.height;

		err = os::primitives::display.Graphics_Read_Setting(
			layer_id,
			lcd_layer.frame_buffer,
			this->lcd.width * lcd_layer.pixel_bytes,
			convert_graphics_format(lcd_layer.buffer_format),
			get_swap_setting(lcd_layer.buffer_format),
			&rect
		);
//		if(err != DisplayBase::GRAPHICS_OK) {
//			return false;
//		}

		err = os::primitives::display.Graphics_Start(layer_id);
		if(err != DisplayBase::GRAPHICS_OK) {
			return false;
		}
		Thread::wait(50);

		switch(this->lcd.type) {
		case LCDTYPE_4_3INCH:
			{
				DigitalOut lcd_cntrst(P8_15, 1);
			}
			break;
		default:
			break;
		}

		using_layer.push_back(lcd_layer.id);

		return true;
	}

	bool Lcd::stop(lcd_layer_id_t id) {
		if( using_layer.end() == std::find( using_layer.begin(), using_layer.end(), id ) ) {
			return false;
		}
		DisplayBase::graphics_error_t err;
		err = os::primitives::display.Graphics_Stop(convert_layer_id(id));
		if(err != DisplayBase::GRAPHICS_OK) {
			return false;
		}
		using_layer.remove(id);
		return true;
	}

	bool Lcd::update(lcd_layer_t lcd_layer) {
		if( using_layer.end() == std::find( using_layer.begin(), using_layer.end(), lcd_layer.id ) ) {
			return false;
		}

		DisplayBase::graphics_error_t err;
		err = os::primitives::display.Graphics_Read_Change(
			convert_layer_id(lcd_layer.id),
			lcd_layer.frame_buffer);
		if(err != DisplayBase::GRAPHICS_OK) {
			return false;
		}

		return true;
	}

	bool Lcd::close(void) {
		bool ret = true;

		while(!using_layer.empty()) {
			ret &= stop(using_layer.front());
		}

		switch(this->lcd.type) {
		case LCDTYPE_4_3INCH:
			{
				DigitalOut lcd_cntrst(P8_15, 0);
				DigitalOut lcd_blon(P8_1, 0);
				DigitalOut lcd_pwon(P7_15, 0);
			}
			break;
		default:
			break;
		}

		return ret;
	}

}}	// namespace os::lcd::

namespace os { namespace jpeg {

	JPEG_Converter::wr_rd_format_t convert_jpeg_format(jpeg_pixel_format_t format) {
		switch(format) {
		case JPEG_PIXELFORMAT_YCBCR422:	return JPEG_Converter::WR_RD_YCbCr422;
		case JPEG_PIXELFORMAT_ARGB8888:	return JPEG_Converter::WR_RD_ARGB8888;
		case JPEG_PIXELFORMAT_RGB565:	return JPEG_Converter::WR_RD_RGB565;
		default:						return JPEG_Converter::WR_RD_YCbCr422;
		}
	}

	JPEG_Converter::wr_rd_swa_t get_swap_setting(jpeg_pixel_format_t format) {
		switch(format) {
		case JPEG_PIXELFORMAT_YCBCR422:	return JPEG_Converter::WR_RD_WRSWA_32_16_8BIT;
		case JPEG_PIXELFORMAT_ARGB8888:	return JPEG_Converter::WR_RD_WRSWA_32BIT;
		case JPEG_PIXELFORMAT_RGB565:	return JPEG_Converter::WR_RD_WRSWA_32_16BIT;
		default:						return JPEG_Converter::WR_RD_WRSWA_NON;
		}
	}

	int Jpeg::encode(jpeg_convert_data_t encode_data) {
		JPEG_Converter::bitmap_buff_info_t bitmap_buff_info;
		JPEG_Converter::encode_options_t   encode_options;
		size_t encode_size;

		bitmap_buff_info.width				= encode_data.width;
		bitmap_buff_info.height				= encode_data.height;
		bitmap_buff_info.format				= convert_jpeg_format(encode_data.pixel_format);
		bitmap_buff_info.buffer_address		= encode_data.src.buf;

		encode_options.encode_buff_size		= encode_data.src.len;
		encode_options.input_swapsetting	= JPEG_Converter::WR_RD_WRSWA_32_16_8BIT;

		dcache_clean(encode_data.src.buf, encode_data.src.len);
		dcache_invalid(encode_data.dst.buf, encode_data.dst.len);
		if (Jcu.encode(&bitmap_buff_info, encode_data.dst.buf,
			&encode_size, &encode_options) != JPEG_Converter::JPEG_CONV_OK) {
			return -1;
		}
		dcache_invalid(encode_data.dst.buf, encode_data.dst.len);
		return encode_size;
	}

	bool Jpeg::decode(jpeg_convert_data_t decode_data) {
		JPEG_Converter::bitmap_buff_info_t bitmap_buff_info;
		JPEG_Converter::decode_options_t   decode_options;
		JPEG_Converter::jpeg_conv_error_t error;

		bitmap_buff_info.width				= decode_data.width;
		bitmap_buff_info.height				= decode_data.height;
		bitmap_buff_info.format				= convert_jpeg_format(decode_data.pixel_format);
		bitmap_buff_info.buffer_address		= decode_data.dst.buf;

		if(bitmap_buff_info.format == JPEG_Converter::WR_RD_YCbCr422) {
			decode_options.output_cb_cr_offset = JPEG_Converter::CBCR_OFFSET_128;
		} else {
			decode_options.output_cb_cr_offset = JPEG_Converter::CBCR_OFFSET_0;
		}
		decode_options.output_swapsetting = get_swap_setting(decode_data.pixel_format);
		decode_options.alpha = decode_data.alpha;

		dcache_clean(decode_data.src.buf, decode_data.src.len);
		dcache_invalid(decode_data.dst.buf, decode_data.dst.len);
		error = Jcu.decode(decode_data.src.buf, &bitmap_buff_info, &decode_options);
		if(error != JPEG_Converter::JPEG_CONV_OK) {
			return false;
		}
		dcache_invalid(decode_data.dst.buf, decode_data.dst.len);
		return true;
	}

}}	// namespace os::jpeg::


namespace os { namespace graphics {
	enum {
		FRAME_LEFT	 = (1<<0),
		FRAME_TOP	 = (1<<1),
		FRAME_RIGHT	 = (1<<2),
		FRAME_BOTTOM = (1<<3),
	};

	/**
	 * @private
	 * 0から遠い方の最も近い整数値に丸める。(C99のlroundfと等価)
	 * @param x 丸めるべき浮動小数点値
	 * @return 丸められた整数値
	 */
	static
	int roundf2i(float x)
	{
		return (x >= 0.0f) ? (int)(x + 0.5f) : (int)(x - 0.5f);
	}

	/**
	 * @private
	 * 反転(ミラー)を考慮した範囲チェックを行い、範囲外のとき真を返す。
	 * x = max(|a|,|b|), y = min(|a|,|b|)
	 * とするとき、(x < s)または(-x >= e)のとき真を返す。
	 * filledが偽の場合、(-y < s)かつ(y >= e)の場合も真を返す。
	 * @param a 値1
	 * @param b 値2
	 * @param s 最低値
	 * @param e 最高値+1
	 * @param filled 0を含む中央部を範囲内とみなすかどうか
	 * @retval 1 範囲外
	 * @retval 0 範囲内
	 */
	static
	int outOfMirroredRange(int a, int b, int s, int e, int filled)
	{
		int a_abs = abs(a);
		int b_abs = abs(b);
		int x;
		int y;

		if (a_abs < b_abs) {
			x = b_abs;
			y = a_abs;
		} else {
			x = a_abs;
			y = b_abs;
		}

		return (((x < s) || (-x >= e)) ||
				((!filled) && (-y < s) && (y >= e))) ? 1 : 0;
	}

	/**
	 * グラフィックス描画クラスのコンストラクタ
	 */
	Graphics::Graphics()
	: m_buf(NULL), m_width(0), m_height(0), m_format(-1)
	{
	}

	/**
	 * グラフィックスの描画を行うフレームバッファの設定を行います。
	 * @param buf フレームバッファとして使用するバッファ。
	 * @param width フレームバッファの横幅(ピクセル数)。
	 * @param height フレームバッファの高さ(ピクセル数)。
	 * @param format フレームバッファのピクセルフォーマット。0 : "rgb565", 1 : "rgb888" のいずれか。
	 * @return 0 : 正常終了、負の値 : 異常終了
	 */
	int Graphics::initFrameBuffer(char *buf, int width, int height, int format)
	{
		if ((buf == NULL) || (width <= 0) || (height <= 0)) {
			// 引数が不正
			return -1;
		}

		if ((format != FORMAT_RGB565) && (format != FORMAT_RGB888)
			&& (format != FORMAT_ARGB8888) && (format != FORMAT_ARGB4444)) {
			// 引数が不正
			return -1;
		}

		m_buf = buf;
		m_width = width;
		m_height = height;
		m_format = format;

		return 0;
	}

	/**
	 * @private
	 * 斜め直線描画の本体(移動量の多い座標がP座標、少ない座標がQ座標とする)
	 * @param ps 始点P座標
	 * @param qs 始点Q座標
	 * @param pe 終点P座標
	 * @param qe 終点Q座標
	 * @param pm P座標の最大値+1
	 * @param qm Q座標の最大値+1
	 * @param pb P座標のバッファ増分(ピクセル数単位で)
	 * @param qb Q座標のバッファ増分(ピクセル数単位で)
	 * @param color 色
	 * @return 常に成功し、0を返す。
	 */
	template <typename T>
	int Graphics::drawLineImpl(int ps, int qs, int pe, int qe, int pm, int qm, int pb, int qb, T color)
	{
		int p0;
		int q0;
		int p1;
		int q1;

		// P座標を昇順に並び替え(Q座標とセットで)
		if (pe < ps) {
			p0 = pe;
			q0 = qe;
			p1 = ps;
			q1 = qs;
		} else {
			p0 = ps;
			q0 = qs;
			p1 = pe;
			q1 = qe;
		}

		if ((p0 >= pm) || (p1 < 0)) {
			// P座標が範囲外のため、何もしない
			return 0;
		}

		// P座標移動量(0以上)
		int dp = p1 - p0;

		// Q座標移動量とQ座標移動ステップ(±1)を計算
		int dq;
		int qstep;
		if (q0 < q1) {
			if ((q1 < 0) || (q0 >= qm)) {
				// Q座標が範囲外のため、何もしない
				return 0;
			}
			dq = q1 - q0;
			qstep = +1;
		} else if (q0 > q1) {
			if ((q0 < 0) || (q1 >= qm)) {
				// Q座標が範囲外のため、何もしない
				return 0;
			}
			dq = q0 - q1;
			qstep = -1;
		} else {
			// 傾きが0の場合(高速化できる特殊ケース)

			if ((q0 < 0) || (q0 >= qm)) {
				// Q座標が範囲外のため、何もしない
				return 0;
			}

			// P座標をバッファ範囲内に絞る
			if (p0 < 0) {
				p0 = 0;
			}
			if (p1 >= pm) {
				p1 = pm - 1;
			}

			// バッファ上の位置を計算
			T *ptr = getBuffer<T>(0, 0) + (pb * p0) + (qb * q0);

			// 描画(すべての点は既にバッファ範囲内に絞られている)
			for (int p = p0; p <= p1; ++p) {
				*ptr = color;
				ptr += pb;
			}
			return 0;
		}

		// ブレゼンハムのアルゴリズム(整数化版)を用いて傾斜を描画

		// 初期誤差を計算
		int error = 2 * dq - dp;

		// 初期位置を設定(バッファ範囲外の可能性あり)
		int p = p0;
		int q = q0;
		T *ptr = getBuffer<T>(0, 0) + (pb * p) + (qb * q);

		// P座標の終点を(pm-1)までに絞る(≧pm 以降の座標はすべてバッファ範囲外になるため)
		if (p1 >= pm) {
			p1 = pm - 1;
		}

		while (p <= p1) {
			if ((p >= 0) && (q >= 0) && (q < qm)) {
				// バッファ範囲内のため、点を描画
				*ptr = color;
			}

			// P座標を一つ進める
			++p;
			ptr += pb;

			// 累積誤差に基づいてQ座標を一つすすめる
			if (error > 0) {
				q += qstep;
				ptr += (qb * qstep);
				error += 2 * (dq - dp);
			} else {
				error += 2 * dq;
			}
		}

		return 0;
	}

	/**
	 * フレームバッファに線を描画します。処理が完了すると呼び出し元に戻ります。失敗するとエラーを返します。
	 * @param startX 線の始点のX座標
	 * @param startY 線の始点のY座標
	 * @param endX 線の終点のX座標
	 * @param endY 線の終点のY座標
	 * @param color 線の色（1ピクセルあたりの色データ）
	 * @return 0 : 正常終了、負の値 : 異常終了
	 */
	int Graphics::drawLine(int startX, int startY, int endX, int endY, uint32_t color)
	{
		if (!m_buf) {
			// 未初期化
			return -2;
		}

		if (abs(endX - startX) >= abs(endY - startY)) {
			// P=X, Q=Y (転置なし) で描画する
			switch (m_format) {
			case FORMAT_RGB565:
			case FORMAT_ARGB4444:
				return drawLineImpl<uint16_t>(startX, startY, endX, endY, m_width, m_height, 1, m_width, (uint16_t)color);
			case FORMAT_RGB888:
			case FORMAT_ARGB8888:
				return drawLineImpl<uint32_t>(startX, startY, endX, endY, m_width, m_height, 1, m_width, color);
			default:
				// フォーマット不正(=> 未初期化)
				return -2;
			}
		} else {
			// P=Y, Q=X (転置あり) で描画する
			switch (m_format) {
			case FORMAT_RGB565:
			case FORMAT_ARGB4444:
				return drawLineImpl<uint16_t>(startY, startX, endY, endX, m_height, m_width, m_width, 1, (uint16_t)color);
			case FORMAT_RGB888:
			case FORMAT_ARGB8888:
				return drawLineImpl<uint32_t>(startY, startX, endY, endX, m_height, m_width, m_width, 1, color);
			default:
				// フォーマット不正(=> 未初期化)
				return -2;
			}
		}
	}

	/**
	 * @private
	 * 矩形描画の本体
	 * @param xs 左端X座標(バッファ範囲内に限定した座標)
	 * @param ys 上端Y座標(バッファ範囲内に限定した座標)
	 * @param xe 右端X座標(バッファ範囲内に限定した座標)
	 * @param ye 下端Y座標(バッファ範囲内に限定した座標)
	 * @param color 色
	 * @param fill 塗りつぶし指定
	 * @param frames 枠の描画対象(FRAME_xxxの組み合わせ)
	 * @return 常に成功し、0を返す。
	 */
	template <typename T>
	int Graphics::drawRectImpl(int xs, int ys, int xe, int ye, T color, int fill, int frames)
	{
		if (fill) {
			T *line = getBuffer<T>(xs, ys);
			for (int yd = ys; yd <= ye; ++yd) {
				T *ptr = line;
				for (int xd = xs; xd <= xe; ++xd) {
					*ptr = color;
					++ptr;
				}
				line += m_width;
			}
			// 塗りつぶし有りの場合はここまで
			return 0;
		}
		if (frames & FRAME_TOP) {
			T *ptr = getBuffer<T>(xs, ys);
			for (int xd = xs; xd <= xe; ++xd) {
				*ptr = color;
				++ptr;
			}
		}
		if ((frames & FRAME_BOTTOM) && (ye > ys)) {
			T *ptr = getBuffer<T>(xs, ye);
			for (int xd = xs; xd <= xe; ++xd) {
				*ptr = color;
				++ptr;
			}
		}
		if (frames & FRAME_LEFT) {
			T *ptr = getBuffer<T>(xs, ys);
			for (int yd = ys; yd <= ye; ++yd) {
				*ptr = color;
				ptr += m_width;
			}
		}
		if ((frames & FRAME_RIGHT) && (xe > xs)) {
			T *ptr = getBuffer<T>(xe, ys);
			for (int yd = ys; yd <= ye; ++yd) {
				*ptr = color;
				ptr += m_width;
			}
		}
		return 0;
	}

	/**
	 * フレームバッファに矩形を描画します。処理が完了すると呼び出し元に戻ります。失敗するとエラーを返します。
	 * @param x 矩形の基点のX座標
	 * @param y 矩形の基点のY座標
	 * @param width 矩形の幅
	 * @param height 矩形の高さ
	 * @param color 矩形の色
	 * @param fill 0 : 塗りつぶさない、それ以外 : 塗りつぶす
	 * @return 0 : 正常終了、負の値 : 異常終了
	 */
	int Graphics::drawRect(int x, int y, int width, int height, uint32_t color, int fill)
	{
		if (!m_buf) {
			// 未初期化
			return -2;
		}

		if ((width < 0) || (height < 0)) {
			// 引数が不正(何もしない)
			return 0;
		}

		if ((width == 0) || (height == 0)) {
			// 描画領域なし(何もしない)
			return 0;
		}

		// 枠フラグの初期化
		int frames = FRAME_LEFT | FRAME_TOP | FRAME_RIGHT | FRAME_BOTTOM;

		// 始点側の描画領域内座標を計算 => (xs, ys)
		int xs = x;
		if (xs < 0) {
			frames &= ~FRAME_LEFT;
			xs = 0;
		}
		int ys = y;
		if (ys < 0) {
			frames &= ~FRAME_TOP;
			ys = 0;
		}

		if ((xs >= m_width) || (ys >= m_height)) {
			// 描画領域なし(何もしない)
			return 0;
		}

		// 終点側の描画領域内座標を計算 => (xe, ye)
		int xe = x + width - 1;
		if (xe >= m_width) {
			frames &= ~FRAME_RIGHT;
			xe = m_width - 1;
		}
		int ye = y + height - 1;
		if (ye >= m_height) {
			frames &= ~FRAME_BOTTOM;
			ye = m_height - 1;
		}

		if ((xe < 0) || (ye < 0)) {
			// 描画領域なし(何もしない)
			return 0;
		}

		// 描画実行
		switch (m_format) {
		case FORMAT_RGB565:
		case FORMAT_ARGB4444:
			return drawRectImpl<uint16_t>(xs, ys, xe, ye, (uint16_t)color, fill, frames);
		case FORMAT_RGB888:
		case FORMAT_ARGB8888:
			return drawRectImpl<uint32_t>(xs, ys, xe, ye, color, fill, frames);
		default:
			// フォーマット不正(=> 未初期化)
			return -2;
		}
	}

	/**
	 * @private
	 * 円弧描画のプロット点判定関数
	 * @param x 判定対象X座標
	 * @param y 判定対象Y座標
	 * @param xs 始点X座標
	 * @param ys 始点Y座標
	 * @param as 始点角度(-360以上360未満)
	 * @param xe 終点X座標
	 * @param ye 終点Y座標
	 * @param ae 終点角度(-360以上360未満)
	 * @return 0 : プロットしない、それ以外 : プロットする
	 */
	static inline
	int judgeArcPlot(int x, int y, int xs, int ys, int as, int xe, int ye, int ae)
	{
		if (as < 0) {
			as += 360;
		}
		if (ae < 0) {
			ae += 360;
		}

		if (as <= ae) {
			if (ae <= 45) {
				return ((x <= xs) && (y >= ys)) && ((x >= xe) && (y <= ye));
			} else if (as <= 45) {
				return ((x <= xs) && (y >= ys));
			} else {
				return 0;
			}
		} else {
			if (as <= 45) {
				return ((x >= xe) && (y <= ye)) || ((x <= xs) && (y >= ys));
			} else if (ae <= 45) {
				return ((x >= xe) && (y <= ye));
			} else {
				return 1;
			}
		}
	}

	/**
	 * @private
	 * 円弧描画の本体
	 * @param xc 中心X座標
	 * @param yc 中心Y座標
	 * @param r 半径
	 * @param xs 始点X座標(原点を円中心とする座標系にて)
	 * @param ys 始点Y座標(原点を円中心とし、Y軸正方向が上向きの座標系にて)
	 * @param as 始点の角度(0以上360未満)
	 * @param xe 終点X座標(原点を円中心とする座標系にて)
	 * @param ye 終点Y座標(原点を円中心とし、Y軸正方向が上向きの座標系にて)
	 * @param ae 終点の角度(0以上360未満)
	 * @param color 色
	 * @note 終点座標の角度は始点座標の角度と等しいか大きい
	 * @return 常に成功し、0を返す。
	 */
	template <typename T>
	int Graphics::drawArcImpl(int xc, int yc, int r, int xs, int ys, int as, int xe, int ye, int ae, T color)
	{
		/*
			正円のラスタライズでしばしば利用されるブレゼンハムのアルゴリズムの
			改良版である、ミッチェナーのアルゴリズムを利用する。

			参考サイト
			http://atarasevich.blogspot.com/2015/07/on-circle-rasterization-algorithms.html
		*/

		int x = r;
		int y = 0;

		int D = 1 - r;

		while (y <= x) {
			// 45度ずつ分けた8つの区間について、それぞれプロットする

			// 0～45度
			if (judgeArcPlot(x, y, +xs, +ys, as, +xe, +ye, ae)) {
				int xp = xc + x;
				int yp = yc - y;
				if ((0 <= xp) && (xp < m_width) && (0 <= yp) && (yp < m_height)) {
					*getBuffer<T>(xp, yp) = color;
				}
			}

			// 45～90度
			if (judgeArcPlot(x, y, +ye, +xe, 90 - ae, +ys, +xs, 90 - as)) {
				int xp = xc + y;
				int yp = yc - x;
				if ((0 <= xp) && (xp < m_width) && (0 <= yp) && (yp < m_height)) {
					*getBuffer<T>(xp, yp) = color;
				}
			}

			// 90～135度
			if (judgeArcPlot(x, y, +ys, -xs, as - 90, +ye, -xe, ae - 90)) {
				int xp = xc - y;
				int yp = yc - x;
				if ((0 <= xp) && (xp < m_width) && (0 <= yp) && (yp < m_height)) {
					*getBuffer<T>(xp, yp) = color;
				}
			}

			// 135～180度
			if (judgeArcPlot(x, y, -xe, +ye, 180 - ae, -xs, +ys, 180 - as)) {
				int xp = xc - x;
				int yp = yc - y;
				if ((0 <= xp) && (xp < m_width) && (0 <= yp) && (yp < m_height)) {
					*getBuffer<T>(xp, yp) = color;
				}
			}

			// 180～225度
			if (judgeArcPlot(x, y, -xs, -ys, as - 180, -xe, -ye, ae - 180)) {
				int xp = xc - x;
				int yp = yc + y;
				if ((0 <= xp) && (xp < m_width) && (0 <= yp) && (yp < m_height)) {
					*getBuffer<T>(xp, yp) = color;
				}
			}

			// 225～270度
			if (judgeArcPlot(x, y, -ye, -xe, 270 - ae, -ys, -xs, 270 - as)) {
				int xp = xc - y;
				int yp = yc + x;
				if ((0 <= xp) && (xp < m_width) && (0 <= yp) && (yp < m_height)) {
					*getBuffer<T>(xp, yp) = color;
				}
			}

			// 270～315度
			if (judgeArcPlot(x, y, -ys, +xs, as - 270, -ye, +xe, ae - 270)) {
				int xp = xc + y;
				int yp = yc + x;
				if ((0 <= xp) && (xp < m_width) && (0 <= yp) && (yp < m_height)) {
					*getBuffer<T>(xp, yp) = color;
				}
			}

			// 315～360度
			if (judgeArcPlot(x, y, +xe, -ye, -ae, +xs, -ys, -as)) {
				int xp = xc + x;
				int yp = yc + y;
				if ((0 <= xp) && (xp < m_width) && (0 <= yp) && (yp < m_height)) {
					*getBuffer<T>(xp, yp) = color;
				}
			}

			if (D <= 0) {
				++y;
				D += 2 * y + 1;
			} else {
				--x;
				++y;
				D += 2 * (y - x) + 1;
			}
		}

		return 0;
	}

	/**
	 * フレームバッファに弧を描画します。処理が完了すると呼び出し元に戻ります。失敗するとエラーを返します。
	 * @param centerX 弧の中心点のX座標
	 * @param centerY 弧の中心点のY座標
	 * @param radius 弧の半径
	 * @param startAngle 弧の始点の角度（単位：度）
	 * @param endAngle 弧の終点の角度（単位：度）
	 * @param color 弧の色（1ピクセルあたりの色データ）
	 * @note 角度は反時計回りを正とする。
	 * @note startAngleとendAngleの差が360以上ある場合、単なる1周の正円を描画する。
	 * @note startAngleがendAngleより大きい場合、時計回りに描画する。
	 * @return 0 : 正常終了、負の値 : 異常終了
	 */
	int Graphics::drawArc(int centerX, int centerY, int radius, int startAngle, int endAngle, uint32_t color)
	{
		if (!m_buf) {
			// 未初期化
			return -2;
		}

		if (radius < 0) {
			// 引数が不正(何もしない)
			return 0;
		}

		if (((centerX + radius) < 0) || ((centerX - radius) >= m_width) ||
			((centerY + radius) < 0) || ((centerY - radius) >= m_height)) {
			// 円を囲む矩形がバッファ範囲外、何もしない
			return 0;
		}

		if (startAngle > endAngle) {
			// 角度の入れ替え
			int angle = startAngle;
			startAngle = endAngle;
			endAngle = angle;
		}

		int diffAngle = endAngle - startAngle;
		if (diffAngle >= 360) {
			// 正円描画に置き換え
			return drawCircle(centerX, centerY, radius, color, 0);
		} else if (diffAngle == 0) {
			// 角度なし(=> 何もしない)
			return 0;
		}

		// 開始角度、終了角度をそれぞれ正規化
		startAngle %= 360;
		if (startAngle < 0) {
			startAngle += 360;
		}
		endAngle = startAngle + diffAngle;
		if (endAngle >= 360) {
			endAngle -= 360;
		}

		// 開始座標、終了座標を計算(原点を円中心とし、Y軸正方向が上向きの座標系で)
		float startRad = startAngle * (atan2f(1.0f, 1.0f) * 4 / 180);
		int xs = roundf2i(cosf(startRad) * radius);
		int ys = roundf2i(sinf(startRad) * radius);
		float endRad = endAngle * (atan2f(1.0f, 1.0f) * 4 / 180);
		int xe = roundf2i(cosf(endRad) * radius);
		int ye = roundf2i(sinf(endRad) * radius);

		switch (m_format) {
		case FORMAT_RGB565:
		case FORMAT_ARGB4444:
			return drawArcImpl<uint16_t>(centerX, centerY, radius, xs, ys, startAngle, xe, ye, endAngle, (uint16_t)color);
		case FORMAT_RGB888:
		case FORMAT_ARGB8888:
			return drawArcImpl<uint32_t>(centerX, centerY, radius, xs, ys, startAngle, xe, ye, endAngle, color);
		default:
			// フォーマット不正(=> 未初期化)
			return -2;
		}
	}

	/**
	 * フレームバッファに円を描画します。処理が完了すると呼び出し元に戻ります。失敗するとエラーを返します。
	 * @param centerX 円の中心点のX座標
	 * @param centerY 円の中心点のY座標
	 * @param radius 円の半径
	 * @param color 円の色（1ピクセルあたりの色データ）
	 * @param fill 0 : 塗りつぶさない、それ以外 : 塗りつぶす
	 * @return 0 : 正常終了、負の値 : 異常終了
	 */
	int Graphics::drawCircle(int centerX, int centerY, int radius, uint32_t color, int fill)
	{
		// X方向とY方向の半径が等しい楕円として処理する
		return drawEllipse(centerX, centerY, radius, radius, color, fill);
	}

	/**
	 * @private
	 * 楕円描画の本体
	 * @param xc 中心X座標
	 * @param yc 中心Y座標
	 * @param a X軸半径
	 * @param b Y軸半径
	 * @param color 色
	 * @param fill 塗りつぶし指定
	 * @return 常に成功し、0を返す。
	 */
	template <typename T>
	int Graphics::drawEllipseImpl(int xc, int yc, int a, int b, T color, int fill)
	{
		/*
			正円のラスタライズでしばしば利用されるブレゼンハムのアルゴリズムを、
			楕円の方程式に当てはめて応用する。

			楕円の方程式を x^2/a^2 + y^2/b^2 = 1 とする。

			ある点(x,y)が楕円の[内側|円周上|外側]にあるかを求める方程式をD(x,y)とすると、
			D(x,y) := x^2*b^2 + y^2*a^2 - a^2*b^2

			楕円のうち x=[a..0], y=[0..b] となる範囲(4分の1)の描画のみに注目する。
			(残り4分の3は、この4分の1のミラーで計算できるため)

			ある点(x,y)をプロットしたのち、次にプロットすべき点は
			点P=(x-1,y+1) 点Q=( x ,y+1)
			点R=(x-1, y )
			のいずれかである。次にプロットすべき点は、まず
			D(x-1,y+1)の値によって選択される。

			D(x-1,y+1) = x^2*b^2 + y^2*a^2 - a^2*b^2 + b^2*(1-2*x) + a^2*(1+2*y)
					= D(x,y) + b^2*(1-2*x) + a^2*(1+2*y)
			が、
			(ア) 負のとき  点Pまたは点Q
			(イ) 零のとき  点P (ちょうど点Pが円周上)
			(ウ) 正のとき  点Pまたは点R

			(ア)のとき、
			d = |D(x,y+1)| - |D(x-1,y+1)|
			= D(x-1,y+1) + D(x,y+1)
			= 2*D(x-1,y+1) - b^2*(1-2*x)
			とすると、
			d >	 0 のとき、点Pの方が点Qより円周に近いので、点Pをプロットする。
			d <= 0 のとき、点Qをプロットする。

			(イ)のとき、点Pをプロットする。

			(ウ)のとき、
			d = |D(x-1,y+1)| - |D(x-1,y)|
			= D(x-1,y+1) + D(x-1,y)
			= 2*D(x-1,y+1) - a^2*(1+2*y)
			とすると、
			d <	 0 のとき、点Pの方が点Rより円周に近いので、点Pをプロットする。
			d >= 0 のとき、点Rをプロットする。
		*/

		const int asq = a * a;
		const int bsq = b * b;

		// (x,y)=(a,0) における D(x-1,y+1) を計算
		long long int D = bsq * (1 - 2 * a) + asq * (1 + 2 * 0);

		int x = a;
		int y = 0;

		// fillの場合は、最後に処理したY座標を覚えておく
		// (ある一つのY座標に到達した初回でX方向に塗りつぶし、
		//	その後の同Y座標では塗りつぶしが不要なため)
		int ylast = -1;

		while (x > 0) {
			// 中心座標を足す
			int xp = xc + x;
			int yp = yc + y;
			int xm = xc - x;
			int ym = yc - y;
			int yp_in = (0 <= yp) && (yp < m_height);
			int ym_in = (0 <= ym) && (ym < m_height);

			if (fill) {
				// 塗りつぶし有り
				if (y > ylast) {
					ylast = y;

					// X座標の範囲を絞る
					if (xp >= m_width) {
						xp = m_width - 1;
					}
					if (xm < 0) {
						xm = 0;
					}

					if (ym_in) {
						// 上側
						T *ptr = getBuffer<T>(xm, ym);
						for (int x = xm; x <= xp; ++x) {
							*ptr = color;
							++ptr;
						}
					}

					if (yp_in) {
						// 下側
						T *ptr = getBuffer<T>(xm, yp);
						for (int x = xm; x <= xp; ++x) {
							*ptr = color;
							++ptr;
						}
					}
				}
			} else {
				// 塗りつぶし無し(円周のみプロット)
				int xp_in = (0 <= xp) && (xp < m_width);
				int xm_in = (0 <= xm) && (xm < m_width);

				if (xp_in && yp_in) {
					*getBuffer<T>(xp, yp) = color;	// 第一象限
				}
				if (xm_in && yp_in) {
					*getBuffer<T>(xm, yp) = color;	// 第二象限
				}
				if (xm_in && ym_in) {
					*getBuffer<T>(xm, ym) = color;	// 第三象限
				}
				if (xp_in && ym_in) {
					*getBuffer<T>(xp, ym) = color;	// 第四象限
				}
			}

			if (D < 0) {
				// 円周は 点P(x-1,y-1) と 点Q(x,y-1) の間を通る
				long long int d = 2 * D - bsq * (1 - 2 * x);

				if (d <= 0) {
					// 点Q(x,y-1)の方が近いか等距離
					++y;
					D += asq * (1 + 2 * y);
					continue;
				}
			} else if (D > 0) {
				// 円周は 点P(x-1,y-1) と 点R(x-1,y) の間を通る
				long long int d = 2 * D - asq * (1 + 2 * y);

				if (d >= 0) {
					// 点R(x-1,y)の方が近いか等距離
					--x;
					D += bsq * (1 - 2 * x);
					continue;
				}
			}

			// 円周はちょうど 点P(x-1,y-1) を通るか、または点Pに近い
			--x;
			++y;
			D += asq * (1 + 2 * y) + bsq * (1 - 2 * x);
		}

		// x=0 のときは特殊扱い 
		// (yは現在値からbまで全てプロット)

		if ((0 <= xc) && (xc < m_width)) {
			int yp = yc + y;
			int ym = yc - y;
			T *ptrp = getBuffer<T>(xc, yp);
			T *ptrm = getBuffer<T>(xc, ym);

			for (; y <= b; ++y, ++yp, --ym) {
				if ((0 <= yp) && (yp < m_height)) {
					*ptrp = color;
				}
				ptrp += m_width;
				if ((0 <= ym) && (ym < m_height)) {
					*ptrm = color;
				}
				ptrm -= m_width;
			}
		}

		return 0;
	}

	/**
	 * フレームバッファに楕円を描画します。処理が完了すると呼び出し元に戻ります。失敗するとエラーを返します。
	 * @param centerX 楕円の中心点のX座標
	 * @param centerY 楕円の中心点のY座標
	 * @param radiusX 楕円のX方向の半径
	 * @param radiusY 楕円のY方向の半径
	 * @param color 楕円の色（1ピクセルあたりの色データ）
	 * @param fill 0 : 塗りつぶさない、それ以外 : 塗りつぶす
	 * @return 0 : 正常終了、負の値 : 異常終了
	 */
	int Graphics::drawEllipse(int centerX, int centerY, int radiusX, int radiusY, uint32_t color, int fill)
	{
		if (!m_buf) {
			// 未初期化
			return -2;
		}

		if ((radiusX < 0) || (radiusY < 0)) {
			// 引数が不正(何もしない)
			return 0;
		}

		if (((centerX + radiusX) < 0) || ((centerX - radiusX) >= m_width) ||
			((centerY + radiusY) < 0) || ((centerY - radiusY) >= m_height)) {
			// 円を囲む矩形がバッファ範囲外、何もしない
			return 0;
		}

		switch (m_format) {
		case FORMAT_RGB565:
		case FORMAT_ARGB4444:
			return drawEllipseImpl<uint16_t>(centerX, centerY, radiusX, radiusY, (uint16_t)color, fill);
		case FORMAT_RGB888:
		case FORMAT_ARGB8888:
			return drawEllipseImpl<uint32_t>(centerX, centerY, radiusX, radiusY, color, fill);
		default:
			// フォーマット不正(=> 未初期化)
			return -2;
		}
	}

	/**
	 * @private
	 * 正多角形描画で使用する斜め直線描画の本体(移動量の多い座標がP座標、少ない座標がQ座標とする)
	 * @param ps 始点P座標(ポリゴン中心を原点とする相対座標にて)
	 * @param qs 始点Q座標(ポリゴン中心を原点とする相対座標にて。常にps以上)
	 * @param pe 終点P座標(ポリゴン中心を原点とする相対座標にて)
	 * @param qe 終点Q座標(ポリゴン中心を原点とする相対座標にて)
	 * @param pc P座標系の原点の、バッファ座標系における座標
	 * @param qc Q座標系の原点の、バッファ座標系における座標
	 * @param pm P座標の最大値+1 (ポリゴン中心を原点とする相対座標にて)
	 * @param qm Q座標の最大値+1 (ポリゴン中心を原点とする相対座標にて)
	 * @param pb P座標のバッファ増分(ピクセル数単位で)
	 * @param qb Q座標のバッファ増分(ピクセル数単位で)
	 * @param color 色
	 * @param mirrors 鏡像指定(0:鏡像なし、1:Q軸対称(P座標反転)あり、2:P軸対称(Q座標反転)あり、3:両軸対称あり)
	 * @param fillDir 塗りつぶし方向指定(0:塗りつぶしなし、1:P軸方向に塗りつぶし、2:Q軸方向に塗りつぶし)
	 */
	template <typename T>
	void Graphics::drawPolygonLineImpl(int ps, int qs, int pe, int qe, int pc, int qc, int pm, int qm, int pb, int qb, T color, int mirrors, int fillDir)
	{
		int p0;
		int q0;
		int p1;
		int q1;

		// P座標を昇順に並び替え(Q座標とセットで)
		if (pe < ps) {
			p0 = pe;
			q0 = qe;
			p1 = ps;
			q1 = qs;
		} else {
			p0 = ps;
			q0 = qs;
			p1 = pe;
			q1 = qe;
		}

		if (outOfMirroredRange(p0, p1, -pc, pm, fillDir & 1)) {
			// P座標がミラーを考慮しても範囲外のため、何もしない
			return;
		}

		if (outOfMirroredRange(q0, q1, -qc, qm, fillDir & 2)) {
			// Q座標がミラーを考慮しても範囲外のため、何もしない
			return;
		}

		// P座標移動量(0以上)
		int dp = p1 - p0;

		// Q座標移動量とQ座標移動ステップ(±1)を計算
		int dq;
		int qstep;
		if (q0 <= q1) {
			dq = q1 - q0;
			qstep = +1;
		} else if (q0 > q1) {
			dq = q0 - q1;
			qstep = -1;
		}

		// ブレゼンハムのアルゴリズム(整数化版)を用いて傾斜を描画

		// 初期誤差を計算
		int error = 2 * dq - dp;

		// 初期位置を設定(バッファ範囲外の可能性あり)
		int p = p0;
		int q = q0;
		T *ptr0 = getBuffer<T>(0, 0) + (pb * (pc + p)) + (qb * (qc + q));
		T *ptr1 = getBuffer<T>(0, 0) + (pb * (pc - p)) + (qb * (qc + q));
		T *ptr2 = getBuffer<T>(0, 0) + (pb * (pc + p)) + (qb * (qc - q));
		T *ptr3 = getBuffer<T>(0, 0) + (pb * (pc - p)) + (qb * (qc - q));

		int last = (fillDir == 1) ? (q - qstep) : (p - 1);

		while (p <= p1) {
			// 現在位置がバッファ内であればプロットする
			if ((p >= -pc) && (p < pm)) {
				if ((q >= -qc) && (q < qm)) {
					*ptr0 = color;
				}
				if ((mirrors & 2) && (-q >= -qc) && (-q < qm)) {
					*ptr2 = color;
				}
			}
			if ((mirrors & 1) && (-p >= -pc) && (-p < pm)) {
				if ((q >= -qc) && (q < qm)) {
					*ptr1 = color;
				}
				if ((mirrors & 2) && (-q >= -qc) && (-q < qm)) {
					*ptr3 = color;
				}
			}

			if (fillDir == 1) {
				// P座標方向塗りつぶし
				if (last == q) {
					goto skipFill;
				}
				last = q;

				// P座標を絞り込み(P座標が負の場合、pf0 < pf1になるよう反転する)
				int pf0;
				int pf1;
				if (p < 0) {
					pf0 = p - 1;
					pf1 = -p + 1;
				} else {
					pf0 = -p + 1;
					pf1 = p - 1;
				}
				if (pf0 < -pc) {
					pf0 = -pc;
				}
				if (pf1 >= pm) {
					pf1 = pm - 1;
				}

				if (pf0 <= pf1) {
					T *ptrf0 = ((q >= -qc) && (q < qm)) ?
						(getBuffer<T>(0, 0) + (pb * (pc + pf0)) + (qb * (qc + q))) : NULL;
					T *ptrf1 = ((mirrors & 2) && (-q >= -qc) && (-q < qm)) ?
						(getBuffer<T>(0, 0) + (pb * (pc + pf0)) + (qb * (qc - q))) : NULL;

					if (!ptrf0 && !ptrf1) {
						// +q,-qともに範囲外のため塗りつぶし不要
						goto skipFill;
					}

					for (int pd = pf0; pd <= pf1; ++pd) {
						if (ptrf0) {
							*ptrf0 = color;
							ptrf0 += pb;
						}
						if (ptrf1) {
							*ptrf1 = color;
							ptrf1 += pb;
						}
					}
				}
			} else if (fillDir == 2) {
				// Q座標方向塗りつぶし
				if (last == p) {
					goto skipFill;
				}
				last = p;

				// Q座標を絞り込み(Q座標が負の場合、qf0 < qf1になるよう反転する)
				int qf0;
				int qf1;
				if (q < 0) {
					qf0 = q - 1;
					qf1 = -q + 1;
				} else {
					qf0 = -q + 1;
					qf1 = q - 1;
				}
				if (qf0 < -qc) {
					qf0 = -qc;
				}
				if (qf1 >= qm) {
					qf1 = qm - 1;
				}

				if (qf0 <= qf1) {
					T *ptrf0 = ((p >= -pc) && (p < pm)) ?
						(getBuffer<T>(0, 0) + (pb * (pc + p)) + (qb * (qc + qf0))) : NULL;
					T *ptrf1 = ((mirrors & 1) && (-p >= -pc) && (-p < pm)) ?
						(getBuffer<T>(0, 0) + (pb * (pc - p)) + (qb * (qc + qf0))) : NULL;

					if (!ptrf0 && !ptrf1) {
						// +p,-pともに範囲外のため塗りつぶし不要
						goto skipFill;
					}

					for (int qd = qf0; qd <= qf1; ++qd) {
						if (ptrf0) {
							*ptrf0 = color;
							ptrf0 += qb;
						}
						if (ptrf1) {
							*ptrf1 = color;
							ptrf1 += qb;
						}
					}
				}
			}
	skipFill:

			// P座標を一つ進める
			++p;
			ptr0 += pb;
			ptr1 -= pb;
			ptr2 += pb;
			ptr3 -= pb;

			// 累積誤差に基づいてQ座標を一つすすめる
			if (error > 0) {
				q += qstep;
				ptr0 += (qb * qstep);
				ptr1 += (qb * qstep);
				ptr2 -= (qb * qstep);
				ptr3 -= (qb * qstep);
				error += 2 * (dq - dp);
			} else {
				error += 2 * dq;
			}
		}
	}

	/**
	 * フレームバッファに多角形を描画します。処理が完了すると呼び出し元に戻ります。失敗するとエラーを返します。
	 * @param centerX 多角形の中心点のX座標
	 * @param centerY 多角形の中心点のY座標
	 * @param radius 多角形の中心点と頂点の距離（外接円の半径）
	 * @param sides 多角形の辺の数
	 * @param color 多角形の色（1ピクセルあたりの色データ）
	 * @param fill 0 : 塗りつぶさない、それ以外 : 塗りつぶす
	 * @note sidesが4(つまり正方形)の場合、開始点の角度は45度となり底辺が水平な正方形が描画されます。
	 * @note sidesが4以外の場合、開始点の角度は90度(真上)となり、そこから指定した辺の数を持つ正多角形が描画されます。
	 * @return 0 : 正常終了、負の値 : 異常終了
	 */
	int Graphics::drawPolygon(int centerX, int centerY, int radius, int sides, uint32_t color, int fill)
	{
		if (!m_buf) {
			// 未初期化
			return -2;
		}

		if ((radius < 0) || (sides < 3)) {
			// 引数が不正(何もしない)
			return 0;
		}

		if (sides == 4) {
			// 四角形は特殊扱い(45度始まりのため、矩形描画を流用)
			int halfWidth = (int)floorf(radius / sqrtf(2.0f));
			return drawRect(centerX - halfWidth, centerY - halfWidth, halfWidth * 2 + 1, halfWidth * 2 + 1, color, fill);
		}

		if (((centerX + radius) < 0) || ((centerX - radius) >= m_width) ||
			((centerY + radius) < 0) || ((centerY - radius) >= m_height)) {
			// 外接円を囲む矩形がバッファ範囲外(=> 何もしない)
			return 0;
		}

		// 中心と各辺を結ぶ二等辺三角形の、中心側の角度を求める
		float rad = atan2f(1.0f, 1.0f) * 8 / sides;

		// 開始点は上側、つまり(x,y)=(0,-r)なので、Y軸で線対称な図形となるため、
		// 半分の頂点について処理し、残りはY軸でミラーすればよい。
		// なお、頂点数が偶数の場合はX軸で線対称な図形でもあるので、さらに半分の頂点のみ
		// 処理する。
		int verticies = (sides & 1) ? (sides >> 1) : (sides >> 2);

		int x = 0;			// sinf(rad * 0) * radius
		int y = -radius;	// cosf(rad * 0) * -radius

		for (int vertex = 1; vertex <= verticies; ++vertex) {
			// 次の頂点の位置を求める
			int xg = roundf2i(sinf(rad * vertex) * radius);
			int yg = roundf2i(cosf(rad * vertex) * -radius);

			// 次の頂点へ向けてブレゼンハムのアルゴリズムで辺を描画する
			if ((yg == y) && (xg == x)) {
				// 次の頂点が現在位置上にある(頂点同士が整数化すると重なっている)
				// => プロット必要なし。次の頂点へ。
				if (vertex == 1) {
					// ただしループ初回はその点を描画する(ドット抜け対策)
					(void)drawLine(centerX + x, centerY + y, centerX + x, centerY + y, color);
				}
				continue;
			}

			if (abs(xg - x) >= (yg - y)) {
				// P=X, Q=Y (転置なし) で描画する
				int mirrors = (sides & 1) ? (1) : (3);
				int fillDir = fill ? 1 : 0;
				switch (m_format) {
				case FORMAT_RGB565:
				case FORMAT_ARGB4444:
					drawPolygonLineImpl<uint16_t>(
						x, y, xg, yg,
						centerX, centerY, m_width - centerX, m_height - centerY,
						1, m_width, (uint16_t)color, mirrors, fillDir);
					break;
				case FORMAT_RGB888:
				case FORMAT_ARGB8888:
					drawPolygonLineImpl<uint32_t>(
						x, y, xg, yg,
						centerX, centerY, m_width - centerX, m_height - centerY,
						1, m_width, (uint32_t)color, mirrors, fillDir);
					break;
				default:
					// フォーマット不正(=> 未初期化)
					return -2;
				}
			} else {
				// P=Y, Q=X (転置あり) で描画する
				int mirrors = (sides & 1) ? (2) : (3);
				int fillDir = fill ? 2 : 0;
				switch (m_format) {
				case FORMAT_RGB565:
				case FORMAT_ARGB4444:
					drawPolygonLineImpl<uint16_t>(
						y, x, yg, xg,
						centerY, centerX, m_height - centerY, m_width - centerX,
						m_width, 1, (uint16_t)color, mirrors, fillDir);
					break;
				case FORMAT_RGB888:
				case FORMAT_ARGB8888:
					drawPolygonLineImpl<uint32_t>(
						y, x, yg, xg,
						centerY, centerX, m_height - centerY, m_width - centerX,
						m_width, 1, (uint32_t)color, mirrors, fillDir);
					break;
				default:
					// フォーマット不正(=> 未初期化)
					return -2;
				}
			}

			// 現在位置を更新
			x = xg;
			y = yg;

			if ((centerY + y) >= m_height) {
				// 現在位置がバッファ範囲外にでた
				// => これ以上の描画処理は不要
				return 0;
			}
		}

		if (sides & 1) {
			// 頂点数が奇数の場合、最後の位置から水平(X方向)に結んで図形を閉じる
			// (ただし、fillした場合はすでに閉じられているので不要)
			// (また、外接円半径が小さすぎてx==0となっている場合も不要)
			if ((!fill) && (x > 0)) {
				int yp = centerY + y;
				// (yp >= m_height)のケースは上記の頂点描画ループでreturn済みのため、
				// ここでは(yp < m_height)が常に真になる。
				if ((0 <= yp)/* && (yp < m_height)*/) {
					(void)drawLine(centerX - x + 1, yp, centerX + x - 1, yp, color);
				}
			}
		} else if (sides & 2) {
			// 頂点数が偶数かつ、4で割って2余る数の場合、
			// 次の頂点へ垂直(Y方向)に結んで図形を閉じる。

			int frames = FRAME_LEFT | FRAME_RIGHT;

			// Y座標の範囲を絞る
			int yp_min = centerY + y + 1;
			if (yp_min < 0) {
				yp_min = 0;
			}
			int yp_max = centerY - y - 1;
			if (yp_max >= m_height) {
				yp_max = m_height - 1;
			}

			// X座標の範囲を絞る
			int xp_min = centerX - x;
			if (xp_min < 0) {
				frames &= ~FRAME_LEFT;
				xp_min = 0;
			}
			int xp_max = centerX + x;
			if (xp_max >= m_width) {
				frames &= ~FRAME_RIGHT;
				xp_max = m_width - 1;
			}

			// プロット(矩形描画を流用)
			switch (m_format) {
			case FORMAT_RGB565:
			case FORMAT_ARGB4444:
				(void)drawRectImpl<uint16_t>(xp_min, yp_min, xp_max, yp_max, (uint16_t)color, fill, frames);
				break;
			case FORMAT_RGB888:
			case FORMAT_ARGB8888:
				(void)drawRectImpl<uint32_t>(xp_min, yp_min, xp_max, yp_max, color, fill, frames);
				break;
			default:
				// フォーマット不正(=> 未初期化)
				return -2;
			}
		}

		return 0;
	}

	int Graphics::drawText(char* text, int x, int y, int size, uint32_t color, uint32_t background) {
		int byte_per_pixel;
		switch (m_format) {
		case FORMAT_RGB565:
		case FORMAT_ARGB4444:
			byte_per_pixel = 2;
			color = (color & 0xFF00) >> 8 | (color & 0x00FF) << 8;
			background = (background & 0xFF00) >> 8 | (background & 0x00FF) << 8;
			break;
		case FORMAT_RGB888:
		case FORMAT_ARGB8888:
			byte_per_pixel = 4;
			color = (color & 0xFF000000) >> 24 | (color & 0x00FF0000) >> 8
					| (color & 0x0000FF00) << 8 | (color & 0x000000FF) << 24;
			background = (background & 0xFF000000) >> 24 | (background & 0x00FF0000) >> 8
					| (background & 0x0000FF00) << 8 | (background & 0x000000FF) << 24;
			break;
		default:
			// フォーマット不正(=> 未初期化)
			return -2;
		}
		AsciiFont ascii((uint8_t*)m_buf, m_width, m_height, m_width*byte_per_pixel, byte_per_pixel, background);
		ascii.DrawStr(text, x, y, color, size);
		return 0;
	}

	template <typename T>
	int Graphics::drawImageImpl(T* image, int xs, int ys, int xe, int ye, int dx, int dy, int width)
	{
			T *dst_line = getBuffer<T>(xs, ys);
			T *src_line = image + dx + dy * width;
			for (int yd = ys; yd <= ye; ++yd) {
				T *dst_ptr = dst_line;
				T *src_ptr = src_line;
				for (int xd = xs; xd <= xe; ++xd) {
					*dst_ptr = *src_ptr;
					++dst_ptr;++src_ptr;
				}
				dst_line += m_width;
				src_line += width;
			}
			return 0;
	}

	int Graphics::drawImage(char* image, int x, int y, int width, int height)
	{
		if (!m_buf) {
			// 未初期化
			return -2;
		}

		if ((image == NULL) || (width < 0) || (height < 0)) {
			// 引数が不正(何もしない)
			return 0;
		}

		if ((width == 0) || (height == 0)) {
			// 描画領域なし(何もしない)
			return 0;
		}

		// 始点側の描画領域内座標を計算 => (xs, ys)
		int xs = x;
		if (xs < 0) {
			xs = 0;
		}
		int ys = y;
		if (ys < 0) {
			ys = 0;
		}

		if ((xs >= m_width) || (ys >= m_height)) {
			// 描画領域なし(何もしない)
			return 0;
		}

		// 終点側の描画領域内座標を計算 => (xe, ye)
		int xe = x + width - 1;
		if (xe >= m_width) {
			xe = m_width - 1;
		}
		int ye = y + height - 1;
		if (ye >= m_height) {
			ye = m_height - 1;
		}

		if ((xe < 0) || (ye < 0)) {
			// 描画領域なし(何もしない)
			return 0;
		}

		// 描画実行
		switch (m_format) {
		case FORMAT_RGB565:
		case FORMAT_ARGB4444:
			return drawImageImpl<uint16_t>((uint16_t*)image, xs, ys, xe, ye, xs-x, ys-y, width);
		case FORMAT_RGB888:
		case FORMAT_ARGB8888:
			return drawImageImpl<uint32_t>((uint32_t*)image, xs, ys, xe, ye, xs-x, ys-y, width);
		default:
			// フォーマット不正(=> 未初期化)
			return -2;
		}
	}

	void Graphics::flush(void) {
		switch (m_format) {
		case FORMAT_RGB565:
		case FORMAT_ARGB4444:
			dcache_clean(m_buf, (m_width*m_height*2 + 31)&~31);
			break;
		case FORMAT_RGB888:
		case FORMAT_ARGB8888:
			dcache_clean(m_buf, (m_width*m_height*4 + 31)&~31);
			break;
		default:
			break;
		}
	}
}}	// namespace os::graphics::
