diff --git a/gl/gl_frame_buffer.sv b/gl/gl_frame_buffer.sv
index 1d1bf1c8af9bd4886145223c82ca4dfacfca43fd..a45ef544e4458bb5a5fc460ab8c69d4ce10ef458 100644
--- a/gl/gl_frame_buffer.sv
+++ b/gl/gl_frame_buffer.sv
@@ -8,15 +8,11 @@ module gl_frame_buffer (
 
 	// VGA Read
 	input logic VGA_REQ,
-	// input logic VGA_FG,
 	input logic [9:0] VGA_X, VGA_Y,
 	output logic [23:0] VGA_RGB,
-	// input logic [19:0] VGA_ADDR,
-	// output logic [15:0] VGA_DATA,
 
 	// GL Read
 	input logic GL_REQ,
-	// input logic GL_FG,
 	input logic [9:0] GL_X, GL_Y,
 	input logic [19:0] GL_ADDR,
 	output logic [15:0] GL_DATA,
@@ -134,6 +130,44 @@ module gl_frame_buffer (
 
 	end
 
+	// GL read steps
+	enum logic [2:0] {
+		s_query_rc, s_query_pix, s_query_pl, s_got_pl, s_fin
+	} gl_readstate, gl_readstate_next;
+
+	logic gl_fb_req, gl_fb_req_in;
+	logic [19:0] gl_fb_addr, gl_fb_addr_in;
+	logic [15:0] gl_fb_data, gl_fb_data_in;
+	logic [15:0] gl_pl_data, gl_pl_data_in;
+	// Pipeline step coordinates
+	logic [19:0] gl_rc_coord, gl_fb_coord, gl_pl_coord;
+	logic [19:0] gl_rc_coord_in, gl_fb_coord_in, gl_pl_coord_in;
+	// PL usage
+	logic gl_from_pl, gl_from_pl_next;
+	// SRAM 8-bit reads cache
+	logic [19:0] gl_fb_prev_addr, gl_fb_prev_addr_in;
+	logic [15:0] gl_fb_prev_data, gl_fb_prev_data_in;
+
+	always_ff @(posedge CLK) begin
+		if(RESET | !GL_REQ) begin
+			gl_readstate <= s_query_rc;
+			gl_fb_req <= 1'b0;
+			gl_fb_addr <= 19'h00000;
+			gl_fb_data <= 16'hCCCC;
+			gl_from_pl <= 1'b0;
+			gl_pl_data <= 16'hCCCC;
+		end else begin
+			gl_readstate <= gl_readstate_next;
+			gl_fb_req <= gl_fb_req_in;
+			gl_fb_addr <= gl_fb_addr_in;
+			gl_fb_data <= gl_fb_data_in;
+			gl_from_pl <= gl_from_pl_next;
+			gl_pl_data <= gl_pl_data_in;
+		end
+		gl_fb_prev_addr <= gl_fb_prev_addr_in;
+		gl_fb_prev_data <= gl_fb_prev_data_in;
+	end
+
 	logic rc_avail, pl_avail;
 
 	always_comb begin
@@ -141,6 +175,8 @@ module gl_frame_buffer (
 		rc_avail = 1'b1;
 		pl_avail = 1'b1;
 
+		// VGA Combinational logic
+
 		// Pipeline shifting
 		vga_rc_en_next = VGA_REQ;
 		vga_fb_en_next = vga_rc_en;
@@ -213,7 +249,7 @@ module gl_frame_buffer (
 
 			if (vga_pl_coord[19:16] >= 4'hA) begin
 				// A palette access
-				rc_avail = 1'b0;
+				pl_avail = 1'b0;
 				if (vga_pl_coord[10]) begin
 					// Upper bits
 					PL_ADDR = vga_fb_data[15:8];
@@ -245,6 +281,108 @@ module gl_frame_buffer (
 			end
 		end else
 			VGA_RGB = 24'h0000ff;
+
+		// GL Combinational logic
+
+		// Default values
+		gl_readstate_next = gl_readstate;
+		gl_fb_req_in = 1'b0;
+		gl_fb_addr_in = gl_fb_addr;
+		gl_fb_data_in = gl_fb_data;
+		gl_from_pl_next = gl_from_pl;
+		gl_pl_data_in = gl_pl_data;
+		gl_fb_prev_data_in = gl_fb_prev_data;
+		gl_fb_prev_addr_in = gl_fb_prev_addr;
+
+		GL_DATA = 16'hXXXX;
+		GL_READY = 1'b0;
+
+		case (gl_readstate)
+			s_query_rc: begin
+				if (~GL_ADDR[19] && rc_avail) begin
+					RC_ADDR = {GL_X, GL_Y[8:0], BUF_ACTIVE};
+					gl_readstate_next = s_query_pix;
+				end
+			end
+
+			s_query_pix: begin
+				if (!gl_fb_data) begin
+					if (RC_DATA_OUT) begin
+						// Read from drawn pixels
+						gl_fb_addr_in = {GL_X, GL_Y[8:0], BUF_ACTIVE};
+						gl_from_pl_next = 1'b0;
+					end else begin
+						// Read from background
+						gl_fb_addr_in =	{4'hA+{2'b0,GL_X[9:8]},
+										 GL_X[7:1], // X without last bit
+										 GL_Y[8:0]};
+
+						gl_from_pl_next = 1'b1;
+					end
+					if (gl_fb_addr_in == gl_fb_prev_addr) begin
+						gl_fb_data_in = gl_fb_prev_data;
+						gl_readstate_next = s_query_pl;
+					end
+				end
+				gl_fb_req_in = 1'b1;
+				if (read2_ready) begin
+					gl_fb_data_in = read2_data;
+					gl_readstate_next = s_query_pl;
+				end
+			end
+
+			s_query_pl: begin
+				gl_fb_prev_addr_in = gl_fb_addr;
+				gl_fb_prev_data_in = gl_fb_data;
+
+				if (gl_fb_addr[19:17] > 3'b100) begin
+					// Need to lookup palette
+					if (pl_avail) begin
+						if (GL_X[0])
+							PL_ADDR = vga_fb_data[15:8];
+						else
+							PL_ADDR = vga_fb_data[7:0];
+						gl_readstate_next = s_query_pl;
+					end
+				end else begin
+					// Done, just return
+					GL_DATA = gl_fb_data;
+					GL_READY = 1'b1;
+					gl_readstate_next = s_fin;
+				end
+			end
+
+			s_got_pl: begin
+				gl_pl_data_in = {PL_DATA_OUT[23:19],
+								 PL_DATA_OUT[15:10],
+								 PL_DATA_OUT[7:3]};
+				GL_DATA = gl_pl_data_in;
+				gl_readstate_next = s_fin;
+			end
+
+			s_fin: begin
+				GL_READY = 1'b1;
+				if (gl_from_pl)
+					GL_DATA = gl_pl_data;
+				else
+					GL_DATA = gl_fb_data;
+
+				// Will leave this state on state-machine reset by falling REQ
+			end
+		endcase
+
+		// Wiring
+		if (GL_ADDR[19]) begin
+			// Direct read from data region
+			read2_req = GL_REQ;
+			read2_addr = GL_ADDR;
+			GL_READY = read2_ready;
+			GL_DATA = read2_data;
+		end else begin
+			read2_req = gl_fb_req;
+			read2_addr = gl_fb_addr;
+		end
+
 	end
 
 endmodule
diff --git a/gl/gl_mgr.sv b/gl/gl_mgr.sv
index 9bd47063ef09c29b7f9cb8e20791b55acc97b337..3dc247c1be46e7270174492eeec341ec43eb6f37 100644
--- a/gl/gl_mgr.sv
+++ b/gl/gl_mgr.sv
@@ -273,9 +273,9 @@ module gl_mgr (
 	// Polygon
 	logic polygon_en, polygon_done;
 	// Memory write connection
-	logic polygon_PAINT_REQ /*synthesis keep*/;
-	logic [9:0] polygon_PAINT_X, polygon_PAINT_Y /*synthesis keep*/;
-	logic [15:0] polygon_PAINT_RGB16 /*synthesis keep*/;
+	logic polygon_PAINT_REQ;
+	logic [9:0] polygon_PAINT_X, polygon_PAINT_Y;
+	logic [15:0] polygon_PAINT_RGB16;
 	// Redraw cache connection
 	logic [18:0] polygon_RC_ADDR;
 	logic polygon_RC_DATA_WR;
@@ -309,6 +309,61 @@ module gl_mgr (
 		.RC_DATA_RD    (rc_data_out1)
 	);
 
+	// Image
+	logic image_en, image_done;
+	// Memory write connection
+	logic image_PAINT_REQ;
+	logic [9:0] image_PAINT_X, image_PAINT_Y;
+	logic [15:0] image_PAINT_RGB16;
+	// Memory read connection
+	logic image_GL_REQ;
+	logic [19:0] image_GL_ADDR;
+	// Redraw cache connection
+	logic [18:0] image_RC_ADDR;
+	logic image_RC_DATA_WR;
+	logic image_RC_WE;
+	logic image_RC_DATA_RD;
+	// Destination coordinate set
+	logic [9:0] image_X[6], image_Y[6];
+	assign image_X[0] = GL_ARG3[19:10];
+	assign image_X[1] = GL_ARG4[19:10];
+	assign image_X[2] = GL_ARG5[19:10];
+	assign image_X[3] = GL_ARG6[19:10];
+	assign image_X[4] = GL_ARG7[19:10];
+	assign image_X[5] = GL_ARG8[19:10];
+	assign image_Y[0] = GL_ARG3[9:0];
+	assign image_Y[1] = GL_ARG4[9:0];
+	assign image_Y[2] = GL_ARG5[9:0];
+	assign image_Y[3] = GL_ARG6[9:0];
+	assign image_Y[4] = GL_ARG7[9:0];
+	assign image_Y[5] = GL_ARG8[9:0];
+	gl_painter_image painter_image(
+		.CLOCK         (CLOCK),
+		.RESET         (RESET),
+		.IMG_BASE      (GL_ARG1),
+		.W             (GL_ARG1[19:10]),
+		.H             (GL_ARG1[9:0]),
+		.TRANSPARENCY  (GL_ARG2[15:0]),
+		.X             (image_X),
+		.Y             (image_Y),
+		.EN            (image_en),
+		.DONE          (image_done),
+		.PAINT_BUFFER  (paint_buffer),
+		.fb_PAINT_REQ  (image_PAINT_REQ),
+		.fb_PAINT_X    (image_PAINT_X),
+		.fb_PAINT_Y    (image_PAINT_Y),
+		.fb_PAINT_RGB16(image_PAINT_RGB16),
+		.fb_PAINT_READY(fb_PAINT_READY),
+		.fb_GL_REQ     (image_GL_REQ),
+		.fb_GL_ADDR    (image_GL_ADDR),
+		.fb_GL_DATA    (fb_GL_RGB),
+		.fb_GL_READY   (fb_GL_READY),
+		.RC_ADDR       (image_RC_ADDR),
+		.RC_DATA_WR    (image_RC_DATA_WR),
+		.RC_WE         (image_RC_WE),
+		.RC_DATA_RD    (rc_data_out1)
+	);
+
 	always_comb begin
 
 		// Route memory access
@@ -317,6 +372,10 @@ module gl_mgr (
 		fb_PAINT_X = 10'hXXX;
 		fb_PAINT_Y = 10'hXXX;
 		fb_PAINT_RGB16 = 16'hXXXX;
+		fb_GL_REQ = 1'b0;
+		fb_GL_ADDR = 20'hXXXXX;
+		fb_GL_X = 10'hXXX;
+		fb_GL_Y = 10'hXXX;
 		rc_addr1 = 8'hXX;
 		rc_data_in1 = 1'bX;
 		rc_we1 = 1'b0;
@@ -363,5 +422,21 @@ module gl_mgr (
 			polygon_en = 1'b0;
 		end
 
+		if (GL_CMD == `GL_CMD_IMAGE) begin
+			image_en = 1'b1;
+			GL_DONE = image_done;
+			fb_PAINT_REQ = image_PAINT_REQ;
+			fb_PAINT_X = image_PAINT_X;
+			fb_PAINT_Y = image_PAINT_Y;
+			fb_PAINT_RGB16 = image_PAINT_RGB16;
+			fb_GL_REQ = image_GL_REQ;
+			fb_GL_ADDR = image_GL_ADDR;
+			rc_addr1 = image_RC_ADDR;
+			rc_data_in1 = image_RC_DATA_WR;
+			rc_we1 = image_RC_WE;
+		end else begin
+			image_en = 1'b0;
+		end
+
 	end
 endmodule
diff --git a/gl/oto_gl_hw.tcl b/gl/oto_gl_hw.tcl
index 12db108ece5460b26c313babada69f5d49915508..a5e334b4e9a8a6f73732e5c38e58fa4034c61fb4 100644
--- a/gl/oto_gl_hw.tcl
+++ b/gl/oto_gl_hw.tcl
@@ -1,12 +1,12 @@
 # TCL File Generated by Component Editor 17.1
-# Sun Dec 03 22:29:28 CST 2017
+# Thu Dec 07 23:23:06 CST 2017
 # DO NOT MODIFY
 
 
 # 
 # oto_gl "otofpga Graphics Library" v1.0
-#  2017.12.03.22:29:28
-# 
+#  2017.12.07.23:23:06
+# otofgpa hardware-accelerated graphics painter
 # 
 
 # 
@@ -18,7 +18,7 @@ package require -exact qsys 16.1
 # 
 # module oto_gl
 # 
-set_module_property DESCRIPTION ""
+set_module_property DESCRIPTION "otofgpa hardware-accelerated graphics painter"
 set_module_property NAME oto_gl
 set_module_property VERSION 1.0
 set_module_property INTERNAL false
@@ -53,6 +53,7 @@ add_fileset_file gl_painter_circle.sv SYSTEM_VERILOG PATH painters/gl_painter_ci
 add_fileset_file gl_painter_polygon.sv SYSTEM_VERILOG PATH painters/gl_painter_polygon.sv
 add_fileset_file gl_blender.sv SYSTEM_VERILOG PATH utils/gl_blender.sv
 add_fileset_file gl_line_scanner.sv SYSTEM_VERILOG PATH utils/gl_line_scanner.sv
+add_fileset_file gl_painter_image.sv SYSTEM_VERILOG PATH painters/gl_painter_image.sv
 
 
 # 
diff --git a/gl/painters/gl_painter_image.sv b/gl/painters/gl_painter_image.sv
new file mode 100644
index 0000000000000000000000000000000000000000..f81d1b6074c14b37c7f46a7e981c8e853a1db179
--- /dev/null
+++ b/gl/painters/gl_painter_image.sv
@@ -0,0 +1,149 @@
+module gl_painter_image (
+	// Clock
+	input logic CLOCK, RESET,
+
+	// Arguments
+	input logic [19:0] IMG_BASE,
+	input logic [9:0] W, H,
+	input logic [9:0] X[6], Y[6],
+
+	// Transparency
+	input logic [15:0] TRANSPARENCY,
+
+	// Status Control
+	input logic EN,
+	output logic DONE,
+
+	input logic PAINT_BUFFER,
+
+	// Memory read connection
+	output logic fb_GL_REQ,
+	output logic [19:0] fb_GL_ADDR,
+	input logic [15:0] fb_GL_DATA,
+	input logic fb_GL_READY,
+
+	// Memory write connection
+	output logic fb_PAINT_REQ,
+	output logic [9:0] fb_PAINT_X, fb_PAINT_Y,
+	output logic [15:0] fb_PAINT_RGB16,
+	input logic fb_PAINT_READY,
+
+	// Redraw cache connection
+	output logic [19:0] RC_ADDR,
+	output logic RC_DATA_WR,
+	output logic RC_WE,
+	input logic RC_DATA_RD
+);
+
+	logic [2:0] i, i_in;
+	logic [19:0] img_ptr, img_ptr_in;
+	logic [9:0] x, y, x_in, y_in;
+	logic [15:0] color, color_in;
+	logic paint_req_in, read_req_in;
+	enum logic[3:0] {
+		s_idle, s_fetch, s_paint, s_fin
+	} state, state_next;
+
+	assign fb_PAINT_X = X[i] + x;
+	assign fb_PAINT_Y = Y[i] + y;
+	assign fb_PAINT_RGB16 = color;
+
+	assign RC_ADDR = {fb_PAINT_X, fb_PAINT_Y[8:0], PAINT_BUFFER};
+	assign RC_DATA_WR = 1'b1;
+
+	assign fb_GL_ADDR = img_ptr;
+
+	always_ff @(posedge CLOCK) begin
+		if(RESET | ~EN) begin
+			state <= s_idle;
+			x <= 0;
+			y <= 0;
+			i <= 0;
+			fb_PAINT_REQ <= 1'b0;
+			fb_GL_REQ <= 1'b0;
+			color <= {5'b0, 6'b0, 5'b11111};
+			img_ptr <= IMG_BASE;
+		end else begin
+			state <= state_next;
+			x <= x_in;
+			y <= y_in;
+			i <= i_in;
+			fb_PAINT_REQ <= paint_req_in;
+			fb_GL_REQ <= read_req_in;
+			color <= color_in;
+			state <= state_next;
+			img_ptr <= img_ptr_in;
+		end
+	end
+
+	always_comb begin
+		// Default values
+		state_next = state;
+		x_in = x;
+		y_in = y;
+		color_in = color;
+		i_in = i;
+		DONE = 1'b0;
+		RC_WE = 1'b0;
+		paint_req_in = 1'b0;
+		read_req_in = 1'b0;
+		img_ptr_in = img_ptr;
+
+		case (state)
+			s_idle: begin
+				state_next = s_fetch;
+				x_in = 0;
+				y_in = 0;
+				i_in = 0;
+				paint_req_in = 1'b0;
+				read_req_in = 1'b1;
+				img_ptr_in = IMG_BASE;
+			end
+
+			s_fetch: begin
+				if (~fb_GL_READY) begin
+					// Continue reading
+					read_req_in = 1'b1;
+				end else begin
+					// Done. Save data and start drawing
+					color_in = fb_GL_DATA;
+					i_in = 0;
+					paint_req_in = 1'b1;
+					state_next = s_paint;
+				end
+			end
+
+			s_paint: begin
+				paint_req_in = 1'b1;
+				RC_WE = 1'b1;
+				if(fb_PAINT_READY) begin
+					// Value written, move to next location
+					i_in = i + 1;
+					if (Y[i_in][9] || i_in == 6) begin
+						// Done. Move to next pixel
+						x_in = x + 1;
+						img_ptr_in = img_ptr + 1;
+						if (x_in == W) begin
+							// Move to next row
+							x_in = 0;
+							y_in = y_in + 1;
+							if (y_in == H) begin
+								// Completely done.
+								paint_req_in = 1'b0;
+								state_next = s_fin;
+								DONE = 1'b1;
+							end
+						end
+					end
+				end
+			end
+
+			s_fin: begin
+				DONE = 1'b1;
+			end
+
+		endcase
+
+	end
+
+endmodule