Skip to content

31. Drawing Textures

Learn how to create and draw textures from emojis, icons, and image files.

31.1 Creating and Drawing Textures

Creating Textures

  • Images displayed on screen are managed by the Texture class
  • There are several ways to create textures:
    • 31.2 Create from emojis
    • 31.3 Create from icons
    • 31.4 Create from image files
    • 31.5 Create from image data
  • Creating textures is costly, so it's usually done before the main loop
  • If creating textures within the main loop, control is needed to prevent recreation every frame

Drawing Textures

  • To draw textures, use the member functions of the Texture class:
    • 31.9 Drawing with top-left coordinate specification .draw()
    • 31.10 Drawing with center coordinate specification .drawAt()
    • 31.11 Drawing with other coordinate specifications .draw(Arg::...)
  • The following classes are provided to represent textures with transformations like scaling, rotation, flipping, and partial extraction:
    • TextureRegion
    • TexturedQuad
    • TexturedCircle
    • TexturedRoundRect
  • These classes are created by member functions of Texture, but can be used almost seamlessly like Texture
// .scaled() returns TextureRegion
// .rotated() returns TexturedQuad
texture.scaled(2.0).rotated(30_deg).drawAt(400, 300);

31.2 Creating from Emojis

  • Siv3D includes over 3,700 emojis compliant with Unicode 15.1
  • Create a texture from an emoji using Texture{ U"emoji"_emoji }
Texture texture{ U"🐈"_emoji };

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture emoji1{ U"🐈"_emoji };
	const Texture emoji2{ U"🍎"_emoji };

	while (System::Update())
	{
		emoji1.drawAt(100, 100);
		emoji1.drawAt(400, 300);

		emoji2.drawAt(200, 300);        
		emoji2.drawAt(Cursor::Pos());
	}
}

31.3 Creating from Icons

  • Siv3D includes over 7,000 icons
  • Create a texture from an icon using Texture{ 0xicon_number_icon, size }
Texture texture{ 0xF0493_icon, 80 };
  • Icon numbers are the hexadecimal codes from Material Design Icons or Font Awesome
  • The same icon designs can be drawn on any platform (Windows, macOS, Linux, Web)
  • Icons are white in color, so you can change the color when drawing using 31.12 color multiplication

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture icon1{ 0xF0493_icon, 100 };
	const Texture icon2{ 0xF0787_icon, 100 };
	const Texture icon3{ 0xF018C_icon, 100 };

	while (System::Update())
	{
		icon1.drawAt(200, 200);
		icon2.drawAt(400, 200, Palette::Seagreen);
		icon3.drawAt(600, 200, ColorF{ 0.2 });
	}
}

31.4 Creating from Image Files

  • To create a texture from an image file, use Texture{ file_path }
  • The file path should be a relative path from the folder where the executable is located (the App folder during development) or an absolute path
    • For example, U"example/windmill.png" refers to the windmill.png file in the example/ folder under the executable folder (App folder)
  • Siv3D supports loading the following 9 image formats:
Format Extension Support
PNG png
JPEG jpg / jpeg / jfif
BMP bmp
SVG svg
GIF gif
TGA tga
PPM ppm / pgm / pbm / pnm
WebP webp
TIFF tif / tiff
DDS dds (Future version)
WBMP wbmp (Future version)
JPEG XL jxl (Future version)

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	// Windmill image
	const Texture texture1{ U"example/windmill.png" };

	// Siv3D-kun (Siv3D's official mascot character) image
	const Texture texture2{ U"example/siv3d-kun.png" };

	while (System::Update())
	{
		texture1.draw(40, 20);

		texture2.draw(400, 100);
	}
}

31.5 Creating from Image Data

  • You can create a texture from image data (Image class) generated or processed by a program
  • Use Texture{ image_data } to create a texture from image data

# include <Siv3D.hpp>

Image MakeImage()
{
	Image image{ 256, 256 };

	for (int32 y = 0; y < image.height(); ++y)
	{
		for (int32 x = 0; x < image.width(); ++x)
		{
			image[y][x] = ColorF{ (x / 255.0), (y / 255.0), 0.0 };
		}
	}

	return image;
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture{ MakeImage() };

	while (System::Update())
	{
		texture.draw();
	}
}

31.6 Texture Size

  • The width (in pixels) of a texture can be obtained with .width(). The return value is of type int32
  • The height (in pixels) of a texture can be obtained with .height(). The return value is of type int32
  • To get both width and height at once, use .size(). The return value is of type Size (Point)

# include <Siv3D.hpp>

void Main()
{
	const Texture texture{ U"example/windmill.png" };
	const Texture emoji{ U"🐈"_emoji };

	Print << texture.width();
	Print << texture.height();
	Print << emoji.size();

	while (System::Update())
	{

	}
}
Output
480
320
(136, 128)

31.7 Empty Texture

  • A Texture object has an empty texture by default
  • An empty texture is a 16 × 16 pixel yellow image that can be treated like a valid texture
  • An empty texture also results when loading emojis, icons, or image files fails
  • To check if a texture is empty, use if (texture.isEmpty()), if (texture), or if (not texture)

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	Texture texture1;

	Print << texture1.isEmpty();

	// Assign a texture
	texture1 = Texture{ U"🐈"_emoji };

	// Specify a non-existent image file
	const Texture texture2{ U"example/aaa.png" };

	if (not texture2)
	{
		Print << U"Failed to load a texture";
	}

	while (System::Update())
	{
		// Draw empty texture (16x16 yellow image)
		texture2.drawAt(400, 300);
	}
}

31.8 Mipmap Generation

  • Mipmaps are a technique where reduced-size images (1/2, 1/4, ...) are pre-generated internally
  • Using mipmaps increases video memory usage by about 30%, but provides the following benefits:
    • Reduced noise and flickering when drawing at reduced sizes (improved image quality)
    • Reduced processing load when drawing at reduced sizes
  • If you never draw at reduced sizes, you might choose not to generate mipmaps
  • In Siv3D, mipmaps are managed internally within the Texture
  • Mipmaps are generated by default when creating textures from emojis or icons
  • When creating textures from image files or Image, you need to explicitly specify TextureDesc::Mipped in the constructor
Texture Creation Method Automatic Mipmap Generation
Create from emojis
Create from icons
Create from image files Requires TextureDesc::Mipped specification
Create from Image Requires TextureDesc::Mipped specification
  • In the following sample, the first texture doesn't generate mipmaps, while the second texture generates mipmaps
  • You can see that using mipmaps reduces noise when scaling down

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
	const Font font{ FontMethod::MSDF, 48 };

	const Texture texture1{ U"example/windmill.png" };
	const Texture texture2{ U"example/windmill.png", TextureDesc::Mipped };

	while (System::Update())
	{
		const double scale = Periodic::Sine0_1(12s);

		font(U"No mipmaps").draw(30, Vec2{ 20, 20 }, ColorF{ 0.2 });
		font(U"Mipmaps").draw(30, Vec2{ 20, 300 }, ColorF{ 0.2 });

		texture1.scaled(scale).draw(240, 20);
		texture2.scaled(scale).draw(240, 300);
	}
}

31.9 Drawing with Top-Left Coordinate Specification

  • To draw a texture with the top-left coordinate specified, use .draw()
Code Description
.draw(color = Palette::White) Draw the texture from coordinate (0, 0)
.draw(x, y, color = Palette::White) Draw the texture from coordinate (x, y)
.draw(pos, color = Palette::White) Draw the texture from coordinate pos
.draw(x, y, Arg::top = top_color, Arg::bottom = bottom_color) Draw with specified top and bottom colors
.draw(x, y, Arg::left = left_color, Arg::right = right_color) Draw with specified left and right colors
.draw(pos, Arg::top = top_color, Arg::bottom = bottom_color) Draw with specified top and bottom colors
.draw(pos, Arg::left = left_color, Arg::right = right_color) Draw with specified left and right colors

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture1{ U"🐈"_emoji };
	const Texture texture2{ U"example/windmill.png" };

	while (System::Update())
	{
		texture1.draw();

		texture2.draw(400, 300);
	}
}

31.10 Drawing with Center Coordinate Specification

  • To draw a texture with the center coordinate specified, use .drawAt()
Code Description
.drawAt(x, y, color = Palette::White) Draw the texture centered at coordinate (x, y)
.drawAt(pos, color = Palette::White) Draw the texture centered at coordinate pos
.drawAt(x, y, Arg::top = top_color, Arg::bottom = bottom_color) Draw with specified top and bottom colors
.drawAt(x, y, Arg::left = left_color, Arg::right = right_color) Draw with specified left and right colors
.drawAt(pos, Arg::top = top_color, Arg::bottom = bottom_color) Draw with specified top and bottom colors
.drawAt(pos, Arg::left = left_color, Arg::right = right_color) Draw with specified left and right colors

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture1{ U"🐈"_emoji };
	const Texture texture2{ U"example/windmill.png" };

	while (System::Update())
	{
		texture1.drawAt(0, 0);

		texture2.drawAt(400, 300);
	}
}

31.11 Drawing with Other Coordinate Specifications

  • To draw a texture with the right edge center position specified, use the following methods:
    • .draw(Arg::topRight = pos, ...)
    • .draw(Arg::topRight(x, y), ...)
  • There are 9 reference positions that can be specified this way:
Reference Position Description
Arg::topLeft Top-left of texture. Same as .draw()
Arg::topCenter Center of top edge
Arg::topRight Top-right
Arg::leftCenter Center of left edge
Arg::center Center. Same as .drawAt()
Arg::rightCenter Center of right edge
Arg::bottomLeft Bottom-left
Arg::bottomCenter Center of bottom edge
Arg::bottomRight Bottom-right

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture1{ U"🐈"_emoji };
	const Texture texture2{ U"example/windmill.png" };

	while (System::Update())
	{
		texture1.draw(Arg::topRight = Vec2{ 800, 0 });

		texture2.draw(Arg::bottomLeft(20, 580));
	}
}

31.12 Drawing with Color Multiplication

31.12.1 Multiplying RGB Components

  • .draw() and .drawAt() allow you to specify a color to multiply with the texture
  • When drawing a texture pixel ColorF{ sr, sg, sb } with color multiplication ColorF{ r, g, b }, the drawn color becomes ColorF{ (sr * r), (sg * g), (sb * b) } (in normal blend mode)
  • By default, Palette::White (ColorF{ 1.0 }) is used as the multiplication color

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture{ U"example/windmill.png" };
	const Texture icon{ 0xF0493_icon, 80 };

	while (System::Update())
	{
		texture.draw(40, 40, ColorF{ 0.4 });

		icon.draw(600, 40, ColorF{ 0.5, 0.0, 0.0 });

		icon.draw(600, 140, ColorF{ 0.0, 0.5, 0.0 });
	}
}

31.12.2 Using Alpha Values

  • You can also use opacity (alpha values)
  • When drawing a texture pixel ColorF{ sr, sg, sb } onto a destination pixel ColorF{ dr, dg, db }, the drawn color becomes ColorF{ (sr * a + dr * (1 - a)), (sg * a + dg * (1 - a)), (sb * a + db * (1 - a)) } (in normal blend mode)

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture{ U"example/windmill.png" };
	const Texture icon{ 0xF0493_icon, 80 };

	while (System::Update())
	{
		texture.draw(40, 40, ColorF{ 1.0, 0.5 });

		icon.draw(500, 40, ColorF{ 0.5, 0.0, 0.0, 0.3 });

		icon.draw(500, 140, ColorF{ 0.0, 0.5, 0.0, 0.3 });
	}
}

31.13 Scaled Drawing

  • To draw a texture with scaling, use the following member functions to create a TextureRegion with scaling applied:
Code Description
.scaled(s) Create a TextureRegion with the texture scaled by s times in both directions
.scaled(sx, sy) Create a TextureRegion with the texture scaled by sx, sy times horizontally and vertically
.resized(size) Create a TextureRegion with the texture's longest side scaled to size (pixels)
.resized(width, height) Create a TextureRegion with the texture scaled to width width (pixels) and height height (pixels)
  • TextureRegion can be drawn just like Texture
  • The cost of creating a TextureRegion from an existing Texture is small, so it can be executed within the main loop without problems

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture{ U"example/windmill.png", TextureDesc::Mipped };
	const Texture emoji{ U"🍎"_emoji };

	while (System::Update())
	{
		texture.scaled(0.25).draw(40, 40);
		texture.scaled(0.8, 0.5).draw(40, 140);
		texture.scaled(2).draw(40, 340);

		emoji.resized(40).draw(500, 40);
		emoji.resized(120, 40).draw(600, 40);
		emoji.resized(40, 120).draw(500, 140);
	}
}

31.14 Drawing Fitted Within a Rectangle

  • To draw a texture as large as possible within a certain size, use the following member functions to create a TextureRegion with scaling applied:
Code Description
.fitted(size) Return a TextureRegion that preserves the texture's aspect ratio while fitting within width size.x and height size.y, scaled to be as large as possible
.fitted(width, height) Return a TextureRegion that preserves the texture's aspect ratio while fitting within width width and height height, scaled to be as large as possible
  • TextureRegion can be drawn just like Texture
  • The cost of creating a TextureRegion from an existing Texture is small, so it can be executed within the main loop without problems

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture1{ U"example/windmill.png", TextureDesc::Mipped };
	const Texture texture2{ U"example/siv3d-kun.png", TextureDesc::Mipped };

	const Rect rect1{ 50, 100, 320, 200 };
	const Rect rect2{ 400, 200, 300 };

	while (System::Update())
	{
		rect1.drawFrame(0, 4, Palette::Seagreen);
		texture1.fitted(rect1.size).drawAt(rect1.center());

		rect2.drawFrame(0, 4, Palette::Seagreen);
		texture2.fitted(rect2.size).drawAt(rect2.center());
	}
}

31.15 Rotated Drawing

  • To draw a texture with rotation, use the following member functions to create a TexturedQuad with rotation applied:
    • .rotated() rotates the texture as if a pin were placed at the center of the texture
    • .rotatedAt() rotates the texture as if a pin were placed at a specified coordinate on the texture
Code Description
.rotated(angle) Create a TexturedQuad with the texture rotated by angle (radians)
.rotatedAt(x, y, angle) Create a TexturedQuad with the texture rotated by angle (radians) around coordinate (x, y)
.rotatedAt(pos, angle) Create a TexturedQuad with the texture rotated by angle (radians) around pos on the texture
  • TexturedQuad can be drawn like Texture
  • The cost of creating a TexturedQuad from an existing Texture is small, so it can be executed within the main loop without problems
  • When drawing a TexturedQuad with coordinate specification, the drawing position is based on the texture before rotation

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture{ U"example/windmill.png" };
	const Texture emoji{ U"🍎"_emoji };

	while (System::Update())
	{
		const double angle = (Scene::Time() * 30_deg);

		texture.rotated(angle).drawAt(200, 300);

		emoji.rotatedAt(Vec2{ 58, 13 }, angle).drawAt(600, 300);
	}
}

31.16 Flipped Drawing

  • To draw a texture with vertical or horizontal flipping, use the following member functions to create a TextureRegion with flipping applied:
Code Description
.flipped() Create a TextureRegion with the texture vertically flipped
.flipped(onOff) Create a TextureRegion with the texture vertically flipped. Flips when onOff is true
.mirrored() Create a TextureRegion with the texture horizontally flipped
.mirrored(onOff) Create a TextureRegion with the texture horizontally flipped. Flips when onOff is true
  • TextureRegion can be drawn just like Texture
  • The cost of creating a TextureRegion from an existing Texture is small, so it can be executed within the main loop without problems

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture emoji{ U"🐈"_emoji };

	while (System::Update())
	{
		emoji.drawAt(100, 100);
		emoji.mirrored().drawAt(300, 100);
		emoji.mirrored(false).drawAt(500, 100);
		emoji.mirrored(true).drawAt(700, 100);

		emoji.drawAt(100, 300);
		emoji.flipped().drawAt(300, 300);
		emoji.flipped(false).drawAt(500, 300);
		emoji.flipped(true).drawAt(700, 300);
	}
}

31.17 Partial Drawing

  • To draw only a rectangular region of a texture, use the following member functions to create a TextureRegion with partial extraction applied:
Code Description
(x, y, w, h) Create a TextureRegion that extracts width w and height h from the texture starting at (x, y)
(rect) Create a TextureRegion that extracts the rect region from the texture
.uv(u, v, w, h) Create a TextureRegion that extracts width w and height h from UV coordinate (u, v) of the texture
.uv(rect) Create a TextureRegion that extracts the UV coordinate rect region from the texture
  • The first two use pixel coordinates, while the latter two use UV coordinates
  • UV coordinates are coordinates where the top-left of the texture is (0.0, 0.0) and the bottom-right is (1.0, 1.0), always ranging from 0.0 to 1.0 regardless of image size
  • When texture texture has size 400 × 200, texture(0.5, 0.0, 0.5, 1.0) is the same as texture(200, 0, 200, 200)

  • TextureRegion can be drawn just like Texture
  • The cost of creating a TextureRegion from an existing Texture is small, so it can be executed within the main loop without problems

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture{ U"example/windmill.png" };
	const Texture emoji{ U"🍎"_emoji };

	while (System::Update())
	{
		// Draw a portion of the image starting from (250, 100) with width 200 and height 150
		texture(250, 100, 200, 150).draw(40, 40);

		// Draw a portion from UV coordinate (0.5, 0.0) with width 0.5 and height 0.75
		emoji.uv(0.5, 0.0, 0.5, 0.75).drawAt(400, 300);
	}
}

31.18 Tiled Drawing

31.18.1 Tiled Drawing

  • To draw textures repeatedly in a tiled pattern, use the following member functions to create a TextureRegion with tiling applied, then draw with the appropriate texture address mode:
Code Description
.mapped(width, height) Create a TextureRegion that tiles the texture with the specified width and height
.mapped(size) Create a TextureRegion that tiles the texture with the specified size
.repeated(X_count, Y_count) Create a TextureRegion that tiles the texture X times horizontally and Y times vertically
  • TextureRegion can be drawn just like Texture
  • The cost of creating a TextureRegion from an existing Texture is small, so it can be executed within the main loop without problems

31.18.2 Texture Address Mode

  • The default texture address mode for 2D drawing is Clamp
  • When trying to draw outside the texture range, that area is filled with the edge color of the texture
  • When UV coordinates specify values smaller than 0.0 or larger than 1.0, they are treated as 0.0 and 1.0 respectively
    • It's like a clock hand trying to point to 13 but stopping at 12
  • Alternatively, when a clock hand tries to point to 13, it can continue past 12, return to 0, and become 1
  • When UV coordinates specify values like 1.1, 2.3, or -0.3, they are treated as 0.1, 0.3, and 0.7 respectively
  • This texture address mode is called Repeat
  • The texture address mode can be changed by setting the sampler state as follows:
    • See Tutorial 48 for more about sampler states

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture emoji{ U"🌳"_emoji };

	while (System::Update())
	{
		{
			// Set texture address mode to repeat
			const ScopedRenderStates2D sampler{ SamplerState::RepeatLinear };

			emoji.mapped(300, 400).draw();

			emoji.repeated(2.5, 4).draw(400, 0);
		}
	}
}

31.19 Rounded Corner Drawing

  • To draw a texture with rounded corners, use the following member function to create a TexturedRoundRect with rounded corners:
Code Description
.rounded(radius) Create a TexturedRoundRect with the texture's corners rounded by radius
  • TexturedRoundRect can be drawn like Texture
  • The cost of creating a TexturedRoundRect from an existing Texture is small, so it can be executed within the main loop without problems

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture{ U"example/windmill.png"};

	while (System::Update())
	{
		texture.rounded(20).drawAt(400, 300);
	}
}

31.20 Combining Operations

  • TextureRegion has member functions for the same operations as Texture, allowing you to combine multiple additional operations to draw textures
    • For example, you can cut out a texture with (x, y, w, h), scale it with .scaled(), and then rotate it with .rotated()
  • TexturedQuad does not have member functions for applying additional operations
Operation Return Value Texture TextureRegion TexturedQuad
.scaled() TextureRegion
.resized() TextureRegion
.fitted() TextureRegion
.rotated() TexturedQuad
.rotatedAt() TexturedQuad
.flipped() TextureRegion
.mirrored() TextureRegion
(x, y, w, h) TextureRegion
.uv(u, v, w, h) TextureRegion
.mapped() TextureRegion
.repeated() TextureRegion
.rounded() TexturedRoundRect

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture{ U"example/windmill.png" };
	const Texture emoji{ U"🐈"_emoji };

	while (System::Update())
	{
		texture
			.uv(0.5, 0.5, 0.5, 0.5)
			.scaled(2.0)
			.rotated(20_deg)
			.draw(20, 20);

		emoji
			.mirrored()
			.flipped()
			.drawAt(600, 300);
	}
}

31.21 Drawing Fitted to Shape

  • You can apply all or part of a texture to Rect, RectF, Circle, Quad, or RoundRect and draw it
  • Use the following member functions to create objects that fit the shape:
Code Description
rect(texture) Create a TexturedQuad by applying a texture (Texture or TextureRegion) to a rectangle (Rect or RectF)
circle(texture) Create a TexturedCircle by applying a texture (Texture or TextureRegion) to a Circle
quad(texture) Create a TexturedQuad by applying a texture (Texture or TextureRegion) to a Quad
roundRect(texture) Create a TexturedRoundRect by applying a texture (Texture or TextureRegion) to a rounded rectangle (RoundRect)
  • TexturedQuad, TexturedCircle, and TexturedRoundRect can be drawn like Texture
  • The cost of creating TexturedQuad, TexturedCircle, and TexturedRoundRect from an existing Texture is small, so it can be executed within the main loop without problems

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture1{ U"example/windmill.png", TextureDesc::Mipped };
	const Texture texture2{ U"example/siv3d-kun.png", TextureDesc::Mipped };

	const Rect rect{ 430, 50, 100, 100 };
	const Circle circle{ 480, 240, 50 };
	const RoundRect roundRect{ 430, 330, 100, 100, 25 };

	while (System::Update())
	{
		Rect{ 50, 50, 350, 400 }(texture1).draw();

		rect
			.drawShadow(Vec2{ 2, 2 }, 12, 1)
			.draw(HSV{ 0, 0.5, 1.0 });
		rect(texture2(90, 3, 110, 110)).draw();

		circle
			.drawShadow(Vec2{ 2, 2 }, 12, 1)
			.draw(HSV{ 240, 0.5, 1.0 });
		circle(texture2(90, 3, 110, 110)).draw();

		roundRect
			.drawShadow(Vec2{ 2, 2 }, 12, 1)
			.draw(HSV{ 120, 0.5, 1.0 });
		roundRect(texture2(90, 3, 110, 110)).draw();
	}
}

31.22 Drawing Fitted to Polygon

  • When applying a texture to a Polygon, use functions like the following to create a Buffer2D from the Polygon, then use the Buffer2D drawing function to draw the texture:

| Code | Description | | polygon.toBuffer2D(offset, size) | Create a Buffer2D that arranges textures of size size with offset as the origin | | polygon.toBuffer2D(Arg::center = offset, size) | Create a Buffer2D that arranges textures of size size with offset as the center |

  • offset controls where the texture is applied in screen coordinates
  • size is the size of the texture to apply
    • If size is smaller than the original texture size, the texture is reduced; if larger, it's enlarged
  • Creating a Buffer2D has a small cost, so if possible, create it before the main loop and reuse the created object
  • With Buffer2D object b and Texture t, draw the texture with b.draw(t)

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture1{ U"example/windmill.png", TextureDesc::Mipped };
	const Texture texture2{ U"example/siv3d-kun.png", TextureDesc::Mipped };

	const Polygon star = Shape2D::Star(180, Vec2{ 200, 200 });
	const Polygon hexagon = Shape2D::Hexagon(60, Vec2{ 480, 380 });

	while (System::Update())
	{
		const double xOffset = (200 + Periodic::Sine1_1(5s) * 80.0);

		// Apply texture to star with (xOffset, 200) as the image center and draw
		star.toBuffer2D(Arg::center(xOffset, 200), texture1.size())
			.draw(texture1);

		hexagon.draw(HSV{ 240, 0.5, 1.0 });

		// Apply texture to hexagon with (515, 562) as the image center and draw
		hexagon.toBuffer2D(Arg::center = Vec2{ 515, 562 }, texture2.size())
			.draw(texture2);
	}
}

31.23 Pre-scaling Large Images

  • Loading high-resolution image files can increase memory usage and degrade runtime performance
  • In such cases, you can scale down the image before creating a texture to save memory and improve drawing speed
  • Load the image file into an Image, scale it down with .scaled(), then create a texture from the resulting Image

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	// Create a texture by scaling the image down to 1/4
	const Texture texture{ Image{ U"example/bay.jpg"}.scaled(0.25) };

	Print << texture.size();

	while (System::Update())
	{
		texture.draw();
	}
}

31.24 Texture Drawing Issues

31.24.1 Pixel Art Loses Pixelated Feel When Enlarged

  • With the default sampler state, textures are smoothly interpolated when enlarged
  • This causes pixel art to lose its pixelated feel and appear blurred when enlarged
  • This can be resolved by changing the sampler state to Nearest
    • See Tutorial 48 for more about sampler states

# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture texture{ U"example/spritesheet/siv3d-kun-16.png" };

	while (System::Update())
	{
		{
			texture(20, 0, 20, 28).scaled(8).drawAt(200, 200);
		}

		{
			const ScopedRenderStates2D rs{ SamplerState::ClampNearest };

			texture(20, 0, 20, 28).scaled(8).drawAt(600, 200);
		}
	}
}

31.24.2 Colors from Surrounding Pixels Bleed in Map Tiles

  • When extracting specific map tiles from images containing arranged map tiles and enlarging them or drawing at floating-point coordinates, colors from adjacent map tiles can bleed through
  • This occurs because surrounding pixels are included during the interpolation process
  • There are several countermeasures:
    • Change the sampler state to Nearest
    • Add 1-pixel padding around textures to minimize the impact of bleeding
    • Draw at integer coordinates instead of floating-point coordinates
    • Use Texture2DArray (a feature available from Siv3D v0.8) to treat each map tile as an independent texture

31.24.3 Images Surrounded by Transparency Show Black Outlines When Enlarged

  • When enlarging images surrounded by transparent pixels, like emojis, the surrounding black can bleed through
  • There are several countermeasures:
    • Change the sampler state to Nearest
      • See Tutorial 48 for more about sampler states
    • Use premultiplied alpha rendering
      • Premultiplied alpha rendering will be supported as standard in Siv3D v0.8. The current version requires code like the following:

# include <Siv3D.hpp>

Image PremultiplyAlpha(Image image)
{
	Color* p = image.data();
	const Color* const pEnd = (p + image.num_pixels());

	while (p != pEnd)
	{
		p->r = static_cast<uint8>((static_cast<uint16>(p->r) * p->a) / 255);
		p->g = static_cast<uint8>((static_cast<uint16>(p->g) * p->a) / 255);
		p->b = static_cast<uint8>((static_cast<uint16>(p->b) * p->a) / 255);
		++p;
	}

	return image;
}

void Main()
{
	Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });

	const Texture emoji1{ U"⛄"_emoji };
	const Texture emoji2{ PremultiplyAlpha(Image{ U"⛄"_emoji }) };

	while (System::Update())
	{
		{
			emoji1.scaled(3).drawAt(200, 300);
		}

		// Premultiplied alpha rendering
		{
			const ScopedRenderStates2D rs{ BlendState::Premultiplied };

			emoji2.scaled(3).drawAt(600, 300);
		}
	}
}