Procházet zdrojové kódy

-Moved NinePatch to shader, saves a ton of draw calls rendering UI
-Implemented missing stretch modes, now tile and tile fit work

Juan Linietsky před 8 roky
rodič
revize
f27d2a3355

+ 28 - 61
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -165,6 +165,7 @@ void RasterizerCanvasGLES3::canvas_begin() {
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF5, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF5, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF13, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF13, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
+	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, false);
 
 
 	state.canvas_shader.set_custom_shader(0);
 	state.canvas_shader.set_custom_shader(0);
 	state.canvas_shader.bind();
 	state.canvas_shader.bind();
@@ -179,6 +180,7 @@ void RasterizerCanvasGLES3::canvas_begin() {
 	glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_item_ubo);
 	glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_item_ubo);
 	glBindVertexArray(data.canvas_quad_array);
 	glBindVertexArray(data.canvas_quad_array);
 	state.using_texture_rect = true;
 	state.using_texture_rect = true;
+	state.using_ninepatch = false;
 }
 }
 
 
 void RasterizerCanvasGLES3::canvas_end() {
 void RasterizerCanvasGLES3::canvas_end() {
@@ -187,6 +189,7 @@ void RasterizerCanvasGLES3::canvas_end() {
 	glBindBufferBase(GL_UNIFORM_BUFFER, 0, 0);
 	glBindBufferBase(GL_UNIFORM_BUFFER, 0, 0);
 
 
 	state.using_texture_rect = false;
 	state.using_texture_rect = false;
+	state.using_ninepatch = false;
 }
 }
 
 
 RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map) {
 RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map) {
@@ -259,9 +262,9 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con
 	return tex_return;
 	return tex_return;
 }
 }
 
 
-void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable) {
+void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepatch) {
 
 
-	if (state.using_texture_rect == p_enable)
+	if (state.using_texture_rect == p_enable && state.using_ninepatch == p_ninepatch)
 		return;
 		return;
 
 
 	if (p_enable) {
 	if (p_enable) {
@@ -273,6 +276,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable) {
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 	}
 	}
 
 
+	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, p_ninepatch && p_enable);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_TEXTURE_RECT, p_enable);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_TEXTURE_RECT, p_enable);
 	state.canvas_shader.bind();
 	state.canvas_shader.bind();
 	state.canvas_shader.set_uniform(CanvasShaderGLES3::FINAL_MODULATE, state.canvas_item_modulate);
 	state.canvas_shader.set_uniform(CanvasShaderGLES3::FINAL_MODULATE, state.canvas_item_modulate);
@@ -280,6 +284,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable) {
 	state.canvas_shader.set_uniform(CanvasShaderGLES3::EXTRA_MATRIX, state.extra_matrix);
 	state.canvas_shader.set_uniform(CanvasShaderGLES3::EXTRA_MATRIX, state.extra_matrix);
 
 
 	state.using_texture_rect = p_enable;
 	state.using_texture_rect = p_enable;
+	state.using_ninepatch = p_ninepatch;
 }
 }
 
 
 void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) {
 void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) {
@@ -494,79 +499,41 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
 
 
 				Item::CommandNinePatch *np = static_cast<Item::CommandNinePatch *>(c);
 				Item::CommandNinePatch *np = static_cast<Item::CommandNinePatch *>(c);
 
 
-				_set_texture_rect_mode(true);
+				_set_texture_rect_mode(true, true);
 
 
 				glVertexAttrib4f(VS::ARRAY_COLOR, np->color.r, np->color.g, np->color.b, np->color.a);
 				glVertexAttrib4f(VS::ARRAY_COLOR, np->color.r, np->color.g, np->color.b, np->color.a);
 
 
 				RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(np->texture, np->normal_map);
 				RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(np->texture, np->normal_map);
 
 
-				if (!texture) {
-
-					glVertexAttrib4f(1, np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y);
-					glVertexAttrib4f(2, 0, 0, 1, 1);
-					glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-					continue;
-				}
-
-				Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
-
-				state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
-				state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, false);
-
-#define DSTRECT(m_x, m_y, m_w, m_h) state.canvas_shader.set_uniform(CanvasShaderGLES3::DST_RECT, Color(m_x, m_y, m_w, m_h))
-#define SRCRECT(m_x, m_y, m_w, m_h) state.canvas_shader.set_uniform(CanvasShaderGLES3::DST_RECT, Color((m_x)*texpixel_size.x, (m_y)*texpixel_size.y, (m_w)*texpixel_size.x, (m_h)*texpixel_size.y))
-
-				//top left
-				DSTRECT(np->rect.position.x, np->rect.position.y, np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP]);
-				SRCRECT(np->source.position.x, np->source.position.y, np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP]);
-				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+				Size2 texpixel_size;
 
 
-				//top right
-				DSTRECT(np->rect.position.x + np->rect.size.width - np->margin[MARGIN_RIGHT], np->rect.position.y, np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
-				SRCRECT(np->source.position.x + np->source.size.width - np->margin[MARGIN_RIGHT], np->source.position.y, np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
-				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+				if (!texture) {
 
 
-				//bottom right
-				DSTRECT(np->rect.position.x + np->rect.size.width - np->margin[MARGIN_RIGHT], np->rect.position.y + np->rect.size.height - np->margin[MARGIN_BOTTOM], np->margin[MARGIN_RIGHT], np->margin[MARGIN_BOTTOM]);
-				SRCRECT(np->source.position.x + np->source.size.width - np->margin[MARGIN_RIGHT], np->source.position.y + np->source.size.height - np->margin[MARGIN_BOTTOM], np->margin[MARGIN_RIGHT], np->margin[MARGIN_BOTTOM]);
-				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+					texpixel_size = Size2(1, 1);
 
 
-				//bottom left
-				DSTRECT(np->rect.position.x, np->rect.position.y + np->rect.size.height - np->margin[MARGIN_BOTTOM], np->margin[MARGIN_LEFT], np->margin[MARGIN_BOTTOM]);
-				SRCRECT(np->source.position.x, np->source.position.y + np->source.size.height - np->margin[MARGIN_BOTTOM], np->margin[MARGIN_LEFT], np->margin[MARGIN_BOTTOM]);
-				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+					state.canvas_shader.set_uniform(CanvasShaderGLES3::SRC_RECT, Color(0, 0, 1, 1));
 
 
-				//top
-				DSTRECT(np->rect.position.x + np->margin[MARGIN_LEFT], np->rect.position.y, np->rect.size.width - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
-				SRCRECT(np->source.position.x + np->margin[MARGIN_LEFT], np->source.position.y, np->source.size.width - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
-				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+				} else {
 
 
-				//bottom
-				DSTRECT(np->rect.position.x + np->margin[MARGIN_LEFT], np->rect.position.y + np->rect.size.height - np->margin[MARGIN_BOTTOM], np->rect.size.width - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
-				SRCRECT(np->source.position.x + np->margin[MARGIN_LEFT], np->source.position.y + np->source.size.height - np->margin[MARGIN_BOTTOM], np->source.size.width - np->margin[MARGIN_LEFT] - np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP]);
-				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+					if (np->source != Rect2()) {
+						texpixel_size = Size2(1.0 / np->source.size.width, 1.0 / np->source.size.height);
+						state.canvas_shader.set_uniform(CanvasShaderGLES3::SRC_RECT, Color(np->source.position.x / texture->width, np->source.position.y / texture->height, np->source.size.x / texture->width, np->source.size.y / texture->height));
+					} else {
+						texpixel_size = Size2(1.0 / texture->width, 1.0 / texture->height);
+						state.canvas_shader.set_uniform(CanvasShaderGLES3::SRC_RECT, Color(0, 0, 1, 1));
+					}
+				}
 
 
-				//left
-				DSTRECT(np->rect.position.x, np->rect.position.y + np->margin[MARGIN_TOP], np->margin[MARGIN_LEFT], np->rect.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
-				SRCRECT(np->source.position.x, np->source.position.y + np->margin[MARGIN_TOP], np->margin[MARGIN_LEFT], np->source.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
-				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+				state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
+				state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, false);
+				state.canvas_shader.set_uniform(CanvasShaderGLES3::NP_REPEAT_H, int(np->axis_x));
+				state.canvas_shader.set_uniform(CanvasShaderGLES3::NP_REPEAT_V, int(np->axis_y));
+				state.canvas_shader.set_uniform(CanvasShaderGLES3::NP_DRAW_CENTER, np->draw_center);
+				state.canvas_shader.set_uniform(CanvasShaderGLES3::NP_MARGINS, Color(np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP], np->margin[MARGIN_RIGHT], np->margin[MARGIN_BOTTOM]));
+				state.canvas_shader.set_uniform(CanvasShaderGLES3::DST_RECT, Color(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y));
 
 
-				//right
-				DSTRECT(np->rect.position.x + np->rect.size.width - np->margin[MARGIN_RIGHT], np->rect.position.y + np->margin[MARGIN_TOP], np->margin[MARGIN_RIGHT], np->rect.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
-				SRCRECT(np->source.position.x + np->source.size.width - np->margin[MARGIN_RIGHT], np->source.position.y + np->margin[MARGIN_TOP], np->margin[MARGIN_RIGHT], np->source.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
 				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
 
-				if (np->draw_center) {
-
-					//center
-					DSTRECT(np->rect.position.x + np->margin[MARGIN_LEFT], np->rect.position.y + np->margin[MARGIN_TOP], np->rect.size.x - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->rect.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
-					SRCRECT(np->source.position.x + np->margin[MARGIN_LEFT], np->source.position.y + np->margin[MARGIN_TOP], np->source.size.x - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->source.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
-					glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-				}
-
-#undef SRCRECT
-#undef DSTRECT
-
 				storage->frame.canvas_draw_commands++;
 				storage->frame.canvas_draw_commands++;
 			} break;
 			} break;
 
 

+ 2 - 1
drivers/gles3/rasterizer_canvas_gles3.h

@@ -67,6 +67,7 @@ public:
 		CanvasShadowShaderGLES3 canvas_shadow_shader;
 		CanvasShadowShaderGLES3 canvas_shadow_shader;
 
 
 		bool using_texture_rect;
 		bool using_texture_rect;
+		bool using_ninepatch;
 
 
 		RID current_tex;
 		RID current_tex;
 		RID current_normal;
 		RID current_normal;
@@ -111,7 +112,7 @@ public:
 	virtual void canvas_begin();
 	virtual void canvas_begin();
 	virtual void canvas_end();
 	virtual void canvas_end();
 
 
-	_FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable);
+	_FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false);
 	_FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map);
 	_FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map);
 
 
 	_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs);
 	_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs);

+ 82 - 0
drivers/gles3/shaders/canvas.glsl

@@ -45,6 +45,12 @@ uniform highp mat4 extra_matrix;
 out mediump vec2 uv_interp;
 out mediump vec2 uv_interp;
 out mediump vec4 color_interp;
 out mediump vec4 color_interp;
 
 
+#ifdef USE_NINEPATCH
+
+out highp vec2 pixel_size_interp;
+#endif
+
+
 #ifdef USE_LIGHTING
 #ifdef USE_LIGHTING
 
 
 layout(std140) uniform LightData { //ubo:1
 layout(std140) uniform LightData { //ubo:1
@@ -142,6 +148,12 @@ VERTEX_SHADER_CODE
 
 
 }
 }
 
 
+
+#ifdef USE_NINEPATCH
+
+	pixel_size_interp=abs(dst_rect.zw) * vertex;
+#endif
+
 #if !defined(SKIP_TRANSFORM_USED)
 #if !defined(SKIP_TRANSFORM_USED)
 	outvec = extra_matrix * outvec;
 	outvec = extra_matrix * outvec;
 	outvec = modelview_matrix * outvec;
 	outvec = modelview_matrix * outvec;
@@ -277,6 +289,58 @@ uniform vec4 dst_rect;
 uniform vec4 src_rect;
 uniform vec4 src_rect;
 uniform bool clip_rect_uv;
 uniform bool clip_rect_uv;
 
 
+#ifdef USE_NINEPATCH
+
+in highp vec2 pixel_size_interp;
+
+uniform int np_repeat_v;
+uniform int np_repeat_h;
+uniform bool np_draw_center;
+//left top right bottom in pixel coordinates
+uniform vec4 np_margins;
+
+
+
+float map_ninepatch_axis(float pixel, float draw_size,float tex_pixel_size,float margin_begin,float margin_end,int np_repeat,inout int draw_center) {
+
+
+	float tex_size = 1.0/tex_pixel_size;
+
+	if (pixel < margin_begin) {
+		return pixel * tex_pixel_size;
+	} else if (pixel >= draw_size-margin_end) {
+		return (tex_size-(draw_size-pixel)) * tex_pixel_size;
+	} else {
+		if (!np_draw_center){
+			draw_center--;
+		}
+
+		if (np_repeat==0) { //stretch
+			//convert to ratio
+			float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
+			//scale to source texture
+			return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
+		} else if (np_repeat==1) { //tile
+			//convert to ratio
+			float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
+			//scale to source texture
+			return (margin_begin + ofs) * tex_pixel_size;
+		} else if (np_repeat==2) { //tile fit
+			//convert to ratio
+			float src_area = draw_size - margin_begin - margin_end;
+			float dst_area = tex_size - margin_begin - margin_end;
+			float scale = max(1.0,floor(src_area / max(dst_area,0.0000001) + 0.5));
+
+			//convert to ratio
+			float ratio = (pixel - margin_begin) / src_area;
+			ratio = mod(ratio * scale,1.0);
+			return (margin_begin + ratio * dst_area) * tex_pixel_size;
+		}
+	}
+
+}
+
+#endif
 #endif
 #endif
 
 
 uniform bool use_default_normal;
 uniform bool use_default_normal;
@@ -287,6 +351,22 @@ void main() {
 	vec2 uv = uv_interp;
 	vec2 uv = uv_interp;
 
 
 #ifdef USE_TEXTURE_RECT
 #ifdef USE_TEXTURE_RECT
+
+#ifdef USE_NINEPATCH
+
+	int draw_center=2;
+	uv = vec2(
+				map_ninepatch_axis(pixel_size_interp.x,abs(dst_rect.z),color_texpixel_size.x,np_margins.x,np_margins.z,np_repeat_h,draw_center),
+				map_ninepatch_axis(pixel_size_interp.y,abs(dst_rect.w),color_texpixel_size.y,np_margins.y,np_margins.w,np_repeat_v,draw_center)
+				);
+
+	if (draw_center==0) {
+		color.a=0;
+	}
+
+	uv = uv*src_rect.zw+src_rect.xy; //apply region if needed
+#endif
+
 	if (clip_rect_uv) {
 	if (clip_rect_uv) {
 
 
 		vec2 half_texpixel = color_texpixel_size * 0.5;
 		vec2 half_texpixel = color_texpixel_size * 0.5;
@@ -309,6 +389,8 @@ void main() {
 
 
 #endif
 #endif
 
 
+
+
 	vec3 normal;
 	vec3 normal;
 
 
 #if defined(NORMAL_USED)
 #if defined(NORMAL_USED)

+ 35 - 1
scene/gui/patch_9_rect.cpp

@@ -44,7 +44,7 @@ void NinePatchRect::_notification(int p_what) {
 		texture->get_rect_region(rect, src_rect, rect, src_rect);
 		texture->get_rect_region(rect, src_rect, rect, src_rect);
 
 
 		RID ci = get_canvas_item();
 		RID ci = get_canvas_item();
-		VS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[MARGIN_LEFT], margin[MARGIN_TOP]), Vector2(margin[MARGIN_RIGHT], margin[MARGIN_BOTTOM]), VS::NINE_PATCH_STRETCH, VS::NINE_PATCH_STRETCH, draw_center);
+		VS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[MARGIN_LEFT], margin[MARGIN_TOP]), Vector2(margin[MARGIN_RIGHT], margin[MARGIN_BOTTOM]), VS::NinePatchAxisMode(axis_h), VS::NinePatchAxisMode(axis_v), draw_center);
 	}
 	}
 }
 }
 
 
@@ -62,6 +62,10 @@ void NinePatchRect::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_region_rect"), &NinePatchRect::get_region_rect);
 	ClassDB::bind_method(D_METHOD("get_region_rect"), &NinePatchRect::get_region_rect);
 	ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &NinePatchRect::set_draw_center);
 	ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &NinePatchRect::set_draw_center);
 	ClassDB::bind_method(D_METHOD("get_draw_center"), &NinePatchRect::get_draw_center);
 	ClassDB::bind_method(D_METHOD("get_draw_center"), &NinePatchRect::get_draw_center);
+	ClassDB::bind_method(D_METHOD("set_h_axis_stretch_mode", "mode"), &NinePatchRect::set_h_axis_stretch_mode);
+	ClassDB::bind_method(D_METHOD("get_h_axis_stretch_mode"), &NinePatchRect::get_h_axis_stretch_mode);
+	ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &NinePatchRect::set_v_axis_stretch_mode);
+	ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &NinePatchRect::get_v_axis_stretch_mode);
 
 
 	ADD_SIGNAL(MethodInfo("texture_changed"));
 	ADD_SIGNAL(MethodInfo("texture_changed"));
 
 
@@ -74,6 +78,13 @@ void NinePatchRect::_bind_methods() {
 	ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_TOP);
 	ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_TOP);
 	ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_RIGHT);
 	ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_RIGHT);
 	ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_BOTTOM);
 	ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_BOTTOM);
+	ADD_GROUP("Axis Stretch", "axis_stretch_");
+	ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
+	ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
+
+	BIND_CONSTANT(AXIS_STRETCH_MODE_STRETCH);
+	BIND_CONSTANT(AXIS_STRETCH_MODE_TILE);
+	BIND_CONSTANT(AXIS_STRETCH_MODE_TILE_FIT);
 }
 }
 
 
 void NinePatchRect::set_texture(const Ref<Texture> &p_tex) {
 void NinePatchRect::set_texture(const Ref<Texture> &p_tex) {
@@ -150,6 +161,26 @@ bool NinePatchRect::get_draw_center() const {
 	return draw_center;
 	return draw_center;
 }
 }
 
 
+void NinePatchRect::set_h_axis_stretch_mode(AxisStretchMode p_mode) {
+	axis_h = p_mode;
+	update();
+}
+
+NinePatchRect::AxisStretchMode NinePatchRect::get_h_axis_stretch_mode() const {
+	return axis_h;
+}
+
+void NinePatchRect::set_v_axis_stretch_mode(AxisStretchMode p_mode) {
+
+	axis_v = p_mode;
+	update();
+}
+
+NinePatchRect::AxisStretchMode NinePatchRect::get_v_axis_stretch_mode() const {
+
+	return axis_v;
+}
+
 NinePatchRect::NinePatchRect() {
 NinePatchRect::NinePatchRect() {
 
 
 	margin[MARGIN_LEFT] = 0;
 	margin[MARGIN_LEFT] = 0;
@@ -159,6 +190,9 @@ NinePatchRect::NinePatchRect() {
 
 
 	set_mouse_filter(MOUSE_FILTER_IGNORE);
 	set_mouse_filter(MOUSE_FILTER_IGNORE);
 	draw_center = true;
 	draw_center = true;
+
+	axis_h = AXIS_STRETCH_MODE_STRETCH;
+	axis_v = AXIS_STRETCH_MODE_STRETCH;
 }
 }
 
 
 NinePatchRect::~NinePatchRect() {
 NinePatchRect::~NinePatchRect() {

+ 17 - 0
scene/gui/patch_9_rect.h

@@ -38,11 +38,20 @@ class NinePatchRect : public Control {
 
 
 	GDCLASS(NinePatchRect, Control);
 	GDCLASS(NinePatchRect, Control);
 
 
+public:
+	enum AxisStretchMode {
+		AXIS_STRETCH_MODE_STRETCH,
+		AXIS_STRETCH_MODE_TILE,
+		AXIS_STRETCH_MODE_TILE_FIT,
+	};
+
 	bool draw_center;
 	bool draw_center;
 	int margin[4];
 	int margin[4];
 	Rect2 region_rect;
 	Rect2 region_rect;
 	Ref<Texture> texture;
 	Ref<Texture> texture;
 
 
+	AxisStretchMode axis_h, axis_v;
+
 protected:
 protected:
 	void _notification(int p_what);
 	void _notification(int p_what);
 	virtual Size2 get_minimum_size() const;
 	virtual Size2 get_minimum_size() const;
@@ -61,7 +70,15 @@ public:
 	void set_draw_center(bool p_enable);
 	void set_draw_center(bool p_enable);
 	bool get_draw_center() const;
 	bool get_draw_center() const;
 
 
+	void set_h_axis_stretch_mode(AxisStretchMode p_mode);
+	AxisStretchMode get_h_axis_stretch_mode() const;
+
+	void set_v_axis_stretch_mode(AxisStretchMode p_mode);
+	AxisStretchMode get_v_axis_stretch_mode() const;
+
 	NinePatchRect();
 	NinePatchRect();
 	~NinePatchRect();
 	~NinePatchRect();
 };
 };
+
+VARIANT_ENUM_CAST(NinePatchRect::AxisStretchMode)
 #endif // PATCH_9_FRAME_H
 #endif // PATCH_9_FRAME_H