or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-drawing.mdaudio-sound.mdcore-system.mddrawing-shapes.mdevent-input.mdgame-objects.mdgraphics-display.mdindex.mdinput-devices.mdjoystick-gamepad.mdmath-utils.mdsurface-image.mdtext-font.mdtime-animation.mdtransform-image.md

transform-image.mddocs/

0

# Transform and Image Processing

1

2

Image transformation operations and advanced image processing. Pygame's transform module provides functions for scaling, rotating, flipping images, and applying various filters and effects to surfaces.

3

4

## Capabilities

5

6

### Basic Transformations

7

8

Core image transformation operations for resizing and orientation changes.

9

10

```python { .api }

11

def scale(surface: pygame.Surface, size: tuple[int, int]) -> pygame.Surface:

12

"""

13

Resize surface to new dimensions.

14

15

Args:

16

surface (pygame.Surface): Surface to scale

17

size (tuple[int, int]): New (width, height)

18

19

Returns:

20

pygame.Surface: Scaled surface

21

"""

22

23

def smoothscale(surface: pygame.Surface, size: tuple[int, int]) -> pygame.Surface:

24

"""

25

Scale surface using smooth interpolation (slower but higher quality).

26

27

Args:

28

surface (pygame.Surface): Surface to scale

29

size (tuple[int, int]): New (width, height)

30

31

Returns:

32

pygame.Surface: Smoothly scaled surface

33

"""

34

35

def scale2x(surface: pygame.Surface) -> pygame.Surface:

36

"""

37

Double surface size using 2xSaI algorithm (good for pixel art).

38

39

Args:

40

surface (pygame.Surface): Surface to scale

41

42

Returns:

43

pygame.Surface: Surface scaled to 2x size with smoothing

44

"""

45

46

def rotate(surface: pygame.Surface, angle: float) -> pygame.Surface:

47

"""

48

Rotate surface by angle in degrees.

49

50

Args:

51

surface (pygame.Surface): Surface to rotate

52

angle (float): Rotation angle in degrees (positive = counterclockwise)

53

54

Returns:

55

pygame.Surface: Rotated surface (may be larger to fit rotation)

56

"""

57

58

def rotozoom(surface: pygame.Surface, angle: float, scale: float) -> pygame.Surface:

59

"""

60

Rotate and scale surface simultaneously (more efficient than separate operations).

61

62

Args:

63

surface (pygame.Surface): Surface to transform

64

angle (float): Rotation angle in degrees

65

scale (float): Scale factor (1.0 = original size)

66

67

Returns:

68

pygame.Surface: Rotated and scaled surface

69

"""

70

71

def flip(surface: pygame.Surface, xbool: bool, ybool: bool) -> pygame.Surface:

72

"""

73

Flip surface horizontally and/or vertically.

74

75

Args:

76

surface (pygame.Surface): Surface to flip

77

xbool (bool): Flip horizontally

78

ybool (bool): Flip vertically

79

80

Returns:

81

pygame.Surface: Flipped surface

82

"""

83

```

84

85

### Advanced Transformations

86

87

More complex transformation operations for special effects.

88

89

```python { .api }

90

def chop(surface: pygame.Surface, rect: pygame.Rect) -> pygame.Surface:

91

"""

92

Remove a rectangular area from surface.

93

94

Args:

95

surface (pygame.Surface): Source surface

96

rect (pygame.Rect): Area to remove

97

98

Returns:

99

pygame.Surface: Surface with area removed

100

"""

101

102

def laplacian(surface: pygame.Surface) -> pygame.Surface:

103

"""

104

Apply Laplacian edge detection filter.

105

106

Args:

107

surface (pygame.Surface): Surface to filter

108

109

Returns:

110

pygame.Surface: Filtered surface showing edges

111

"""

112

```

113

114

### Color and Statistical Operations

115

116

Functions for analyzing and manipulating surface colors.

117

118

```python { .api }

119

def average_surfaces(surfaces: list[pygame.Surface], palette = None, search: int = 1) -> pygame.Surface:

120

"""

121

Average multiple surfaces together.

122

123

Args:

124

surfaces (list[pygame.Surface]): List of surfaces to average

125

palette: Color palette for result

126

search (int): Palette search method

127

128

Returns:

129

pygame.Surface: Averaged surface

130

"""

131

132

def average_color(surface: pygame.Surface, rect: pygame.Rect = None, consider_alpha: bool = False) -> pygame.Color:

133

"""

134

Calculate average color of surface or region.

135

136

Args:

137

surface (pygame.Surface): Surface to analyze

138

rect (pygame.Rect, optional): Region to analyze (None for entire surface)

139

consider_alpha (bool): Include alpha channel in calculation

140

141

Returns:

142

pygame.Color: Average color

143

"""

144

145

def grayscale(surface: pygame.Surface) -> pygame.Surface:

146

"""

147

Convert surface to grayscale.

148

149

Args:

150

surface (pygame.Surface): Surface to convert

151

152

Returns:

153

pygame.Surface: Grayscale version of surface

154

"""

155

156

def threshold(dest_surface: pygame.Surface, surface: pygame.Surface, search_color, threshold: tuple = (0,0,0,0), set_color: tuple = (0,0,0,0), set_behavior: int = 1, search_surf: pygame.Surface = None, inverse_set: bool = False) -> int:

157

"""

158

Apply color threshold filter to surface.

159

160

Args:

161

dest_surface (pygame.Surface): Destination for result

162

surface (pygame.Surface): Source surface

163

search_color: Color to threshold against

164

threshold (tuple): RGBA threshold values

165

set_color (tuple): Color to set matching pixels to

166

set_behavior (int): How to apply threshold

167

search_surf (pygame.Surface, optional): Surface to search in

168

inverse_set (bool): Invert the threshold operation

169

170

Returns:

171

int: Number of pixels that matched threshold

172

"""

173

```

174

175

### Optimized Scaling

176

177

Specialized scaling functions for different use cases.

178

179

```python { .api }

180

def smoothscale_by(surface: pygame.Surface, factor: tuple[float, float]) -> pygame.Surface:

181

"""

182

Smooth scale by factor rather than absolute size.

183

184

Args:

185

surface (pygame.Surface): Surface to scale

186

factor (tuple[float, float]): (x_factor, y_factor) scaling factors

187

188

Returns:

189

pygame.Surface: Scaled surface

190

"""

191

192

def get_smoothscale_backend() -> str:

193

"""

194

Get current smoothscale algorithm backend.

195

196

Returns:

197

str: Backend name ('GENERIC', 'MMX', 'SSE', etc.)

198

"""

199

200

def set_smoothscale_backend(backend: str) -> None:

201

"""

202

Set smoothscale algorithm backend.

203

204

Args:

205

backend (str): Backend to use ('GENERIC', 'MMX', 'SSE')

206

"""

207

```

208

209

## Performance Considerations

210

211

Transform operations create new surfaces and can be expensive. Here are optimization strategies:

212

213

### Pre-computation

214

215

```python { .api }

216

# Instead of transforming every frame:

217

# BAD - transforms every frame

218

def update_bad(self):

219

rotated = pygame.transform.rotate(self.image, self.angle)

220

screen.blit(rotated, self.pos)

221

222

# GOOD - pre-compute rotations

223

def precompute_rotations(self):

224

self.rotated_images = {}

225

for angle in range(0, 360, 5): # Every 5 degrees

226

self.rotated_images[angle] = pygame.transform.rotate(self.image, angle)

227

228

def update_good(self):

229

# Use nearest pre-computed rotation

230

nearest_angle = round(self.angle / 5) * 5

231

rotated = self.rotated_images[nearest_angle]

232

screen.blit(rotated, self.pos)

233

```

234

235

### Scaling Guidelines

236

237

```python { .api }

238

# Use scale() for integer scaling when possible

239

scaled_2x = pygame.transform.scale(surface, (width * 2, height * 2))

240

241

# Use smoothscale() for non-integer scaling or when quality matters

242

scaled_smooth = pygame.transform.smoothscale(surface, (new_width, new_height))

243

244

# Use rotozoom() when rotating AND scaling

245

rotated_scaled = pygame.transform.rotozoom(surface, angle, scale_factor)

246

# Instead of:

247

# rotated = pygame.transform.rotate(surface, angle)

248

# scaled = pygame.transform.scale(rotated, new_size) # Less efficient

249

```

250

251

## Usage Examples

252

253

### Basic Image Transformations

254

255

```python

256

import pygame

257

import math

258

259

pygame.init()

260

screen = pygame.display.set_mode((800, 600))

261

clock = pygame.time.Clock()

262

263

# Create test surface

264

original_surface = pygame.Surface((100, 60))

265

original_surface.fill((255, 0, 0))

266

pygame.draw.rect(original_surface, (255, 255, 255), (10, 10, 80, 40))

267

268

angle = 0

269

scale_factor = 1.0

270

271

running = True

272

while running:

273

for event in pygame.event.get():

274

if event.type == pygame.QUIT:

275

running = False

276

277

keys = pygame.key.get_pressed()

278

279

# Control transformations

280

if keys[pygame.K_LEFT]:

281

angle -= 2

282

if keys[pygame.K_RIGHT]:

283

angle += 2

284

if keys[pygame.K_UP]:

285

scale_factor = min(3.0, scale_factor + 0.02)

286

if keys[pygame.K_DOWN]:

287

scale_factor = max(0.1, scale_factor - 0.02)

288

289

screen.fill((50, 50, 50))

290

291

# Original

292

screen.blit(original_surface, (50, 50))

293

294

# Rotated only

295

rotated = pygame.transform.rotate(original_surface, angle)

296

screen.blit(rotated, (200, 50))

297

298

# Scaled only

299

new_size = (int(100 * scale_factor), int(60 * scale_factor))

300

scaled = pygame.transform.scale(original_surface, new_size)

301

screen.blit(scaled, (50, 150))

302

303

# Rotate and scale combined

304

rotozoom = pygame.transform.rotozoom(original_surface, angle, scale_factor)

305

screen.blit(rotozoom, (400, 150))

306

307

# Flipped

308

flipped_h = pygame.transform.flip(original_surface, True, False)

309

flipped_v = pygame.transform.flip(original_surface, False, True)

310

flipped_both = pygame.transform.flip(original_surface, True, True)

311

312

screen.blit(flipped_h, (50, 300))

313

screen.blit(flipped_v, (200, 300))

314

screen.blit(flipped_both, (350, 300))

315

316

# Labels

317

font = pygame.font.Font(None, 24)

318

labels = ["Original", "Rotated", "Scaled", "Rotozoom", "Flip H", "Flip V", "Flip Both"]

319

positions = [(50, 30), (200, 30), (50, 130), (400, 130), (50, 280), (200, 280), (350, 280)]

320

321

for label, pos in zip(labels, positions):

322

text = font.render(label, True, (255, 255, 255))

323

screen.blit(text, pos)

324

325

# Instructions

326

instructions = font.render("Arrow keys: rotate and scale", True, (255, 255, 255))

327

screen.blit(instructions, (50, 500))

328

329

pygame.display.flip()

330

clock.tick(60)

331

332

pygame.quit()

333

```

334

335

### Smooth Animation with Pre-computed Rotations

336

337

```python

338

import pygame

339

import math

340

341

class RotatingSprite:

342

def __init__(self, image_path, x, y):

343

try:

344

self.original_image = pygame.image.load(image_path).convert_alpha()

345

except:

346

# Create fallback surface

347

self.original_image = pygame.Surface((50, 50), pygame.SRCALPHA)

348

pygame.draw.polygon(self.original_image, (255, 255, 255),

349

[(25, 0), (50, 50), (0, 50)])

350

351

self.x = x

352

self.y = y

353

self.angle = 0

354

self.rotation_speed = 60 # degrees per second

355

356

# Pre-compute rotations for smooth animation

357

self.rotated_images = {}

358

self.rotation_step = 3 # degrees between cached rotations

359

for angle in range(0, 360, self.rotation_step):

360

rotated = pygame.transform.rotate(self.original_image, angle)

361

self.rotated_images[angle] = rotated

362

363

def update(self, dt):

364

self.angle += self.rotation_speed * dt

365

self.angle %= 360

366

367

def draw(self, screen):

368

# Find nearest cached rotation

369

nearest_angle = round(self.angle / self.rotation_step) * self.rotation_step

370

nearest_angle %= 360

371

372

rotated_image = self.rotated_images[nearest_angle]

373

374

# Center the rotated image

375

rect = rotated_image.get_rect()

376

rect.center = (self.x, self.y)

377

378

screen.blit(rotated_image, rect)

379

380

pygame.init()

381

screen = pygame.display.set_mode((800, 600))

382

clock = pygame.time.Clock()

383

384

# Create rotating sprites

385

sprites = [

386

RotatingSprite(None, 200, 200), # Will use fallback

387

RotatingSprite(None, 400, 200),

388

RotatingSprite(None, 600, 200),

389

]

390

391

# Give different rotation speeds

392

sprites[0].rotation_speed = 30

393

sprites[1].rotation_speed = 60

394

sprites[2].rotation_speed = 120

395

396

running = True

397

while running:

398

dt = clock.tick(60) / 1000.0

399

400

for event in pygame.event.get():

401

if event.type == pygame.QUIT:

402

running = False

403

404

# Update sprites

405

for sprite in sprites:

406

sprite.update(dt)

407

408

# Draw

409

screen.fill((0, 0, 50))

410

411

for sprite in sprites:

412

sprite.draw(screen)

413

414

# Show FPS

415

font = pygame.font.Font(None, 36)

416

fps_text = font.render(f"FPS: {clock.get_fps():.1f}", True, (255, 255, 255))

417

screen.blit(fps_text, (10, 10))

418

419

pygame.display.flip()

420

421

pygame.quit()

422

```

423

424

### Image Processing Effects

425

426

```python

427

import pygame

428

import random

429

430

def create_test_image():

431

"""Create colorful test image"""

432

surface = pygame.Surface((200, 150))

433

434

# Gradient background

435

for y in range(150):

436

color_value = int(255 * y / 150)

437

pygame.draw.line(surface, (color_value, 100, 255 - color_value), (0, y), (200, y))

438

439

# Add some shapes

440

pygame.draw.circle(surface, (255, 255, 0), (50, 75), 30)

441

pygame.draw.rect(surface, (0, 255, 0), (120, 50, 60, 50))

442

pygame.draw.polygon(surface, (255, 0, 255), [(150, 20), (180, 40), (160, 60), (130, 40)])

443

444

return surface

445

446

pygame.init()

447

screen = pygame.display.set_mode((1000, 700))

448

clock = pygame.time.Clock()

449

450

# Create original image

451

original = create_test_image()

452

453

# Pre-compute some effects

454

effects = {}

455

456

# Scaling effects

457

effects['scaled_2x'] = pygame.transform.scale2x(original)

458

effects['smooth_scaled'] = pygame.transform.smoothscale(original, (300, 225))

459

effects['small_scaled'] = pygame.transform.scale(original, (100, 75))

460

461

# Color effects

462

effects['grayscale'] = pygame.transform.grayscale(original)

463

464

# Get average color

465

avg_color = pygame.transform.average_color(original)

466

effects['avg_color_surface'] = pygame.Surface((200, 150))

467

effects['avg_color_surface'].fill(avg_color)

468

469

# Rotation effects

470

effects['rotated_45'] = pygame.transform.rotate(original, 45)

471

effects['rotozoom'] = pygame.transform.rotozoom(original, 30, 0.7)

472

473

# Flip effects

474

effects['flipped_h'] = pygame.transform.flip(original, True, False)

475

effects['flipped_v'] = pygame.transform.flip(original, False, True)

476

477

# Laplacian edge detection

478

try:

479

effects['edges'] = pygame.transform.laplacian(original)

480

except:

481

effects['edges'] = original.copy() # Fallback if not available

482

483

running = True

484

effect_names = list(effects.keys())

485

current_effect = 0

486

487

while running:

488

for event in pygame.event.get():

489

if event.type == pygame.QUIT:

490

running = False

491

elif event.type == pygame.KEYDOWN:

492

if event.key == pygame.K_LEFT:

493

current_effect = (current_effect - 1) % len(effect_names)

494

elif event.key == pygame.K_RIGHT:

495

current_effect = (current_effect + 1) % len(effect_names)

496

497

screen.fill((40, 40, 40))

498

499

# Draw original

500

screen.blit(original, (50, 50))

501

502

# Draw current effect

503

effect_name = effect_names[current_effect]

504

effect_surface = effects[effect_name]

505

screen.blit(effect_surface, (300, 50))

506

507

# Labels

508

font = pygame.font.Font(None, 36)

509

510

original_label = font.render("Original", True, (255, 255, 255))

511

screen.blit(original_label, (50, 10))

512

513

effect_label = font.render(f"Effect: {effect_name}", True, (255, 255, 255))

514

screen.blit(effect_label, (300, 10))

515

516

# Instructions

517

instructions = font.render("Left/Right arrows to change effect", True, (255, 255, 255))

518

screen.blit(instructions, (50, 400))

519

520

# Show average color info

521

color_info = font.render(f"Average color: {avg_color}", True, (255, 255, 255))

522

screen.blit(color_info, (50, 450))

523

524

pygame.display.flip()

525

clock.tick(60)

526

527

pygame.quit()

528

```

529

530

### Performance Comparison

531

532

```python

533

import pygame

534

import time

535

536

def performance_test():

537

pygame.init()

538

539

# Create test surface

540

test_surface = pygame.Surface((100, 100))

541

test_surface.fill((255, 0, 0))

542

543

iterations = 1000

544

545

# Test regular scale

546

start_time = time.time()

547

for _ in range(iterations):

548

scaled = pygame.transform.scale(test_surface, (200, 200))

549

scale_time = time.time() - start_time

550

551

# Test smooth scale

552

start_time = time.time()

553

for _ in range(iterations):

554

scaled = pygame.transform.smoothscale(test_surface, (200, 200))

555

smoothscale_time = time.time() - start_time

556

557

# Test rotation

558

start_time = time.time()

559

for i in range(iterations):

560

rotated = pygame.transform.rotate(test_surface, i % 360)

561

rotation_time = time.time() - start_time

562

563

# Test rotozoom

564

start_time = time.time()

565

for i in range(iterations):

566

rotozoomed = pygame.transform.rotozoom(test_surface, i % 360, 1.0)

567

rotozoom_time = time.time() - start_time

568

569

print(f"Performance test results ({iterations} iterations):")

570

print(f"Regular scale: {scale_time:.3f} seconds")

571

print(f"Smooth scale: {smoothscale_time:.3f} seconds ({smoothscale_time/scale_time:.1f}x slower)")

572

print(f"Rotation: {rotation_time:.3f} seconds")

573

print(f"Rotozoom: {rotozoom_time:.3f} seconds ({rotozoom_time/rotation_time:.1f}x vs rotation)")

574

575

pygame.quit()

576

577

if __name__ == "__main__":

578

performance_test()

579

```

580

581

### Advanced Threshold Effects

582

583

```python

584

import pygame

585

import random

586

587

def create_color_test_surface():

588

"""Create surface with various colors for threshold testing"""

589

surface = pygame.Surface((300, 200))

590

591

# Create color regions

592

colors = [

593

(255, 0, 0), # Red

594

(0, 255, 0), # Green

595

(0, 0, 255), # Blue

596

(255, 255, 0), # Yellow

597

(255, 0, 255), # Magenta

598

(0, 255, 255), # Cyan

599

(128, 128, 128), # Gray

600

(255, 255, 255), # White

601

]

602

603

# Fill with color patches

604

patch_width = surface.get_width() // 4

605

patch_height = surface.get_height() // 2

606

607

for i, color in enumerate(colors):

608

x = (i % 4) * patch_width

609

y = (i // 4) * patch_height

610

rect = pygame.Rect(x, y, patch_width, patch_height)

611

pygame.draw.rect(surface, color, rect)

612

613

return surface

614

615

pygame.init()

616

screen = pygame.display.set_mode((800, 600))

617

clock = pygame.time.Clock()

618

619

# Create test surface

620

original = create_color_test_surface()

621

622

# Threshold settings

623

threshold_color = [128, 128, 128, 255] # RGBA

624

set_color = [255, 255, 255, 255] # White

625

threshold_tolerance = [50, 50, 50, 0] # RGBA tolerance

626

627

running = True

628

while running:

629

for event in pygame.event.get():

630

if event.type == pygame.QUIT:

631

running = False

632

elif event.type == pygame.KEYDOWN:

633

if event.key == pygame.K_r:

634

threshold_color[0] = random.randint(0, 255)

635

elif event.key == pygame.K_g:

636

threshold_color[1] = random.randint(0, 255)

637

elif event.key == pygame.K_b:

638

threshold_color[2] = random.randint(0, 255)

639

640

# Create threshold result

641

result_surface = pygame.Surface(original.get_size())

642

643

try:

644

num_matches = pygame.transform.threshold(

645

result_surface,

646

original,

647

tuple(threshold_color[:3]),

648

tuple(threshold_tolerance[:3]),

649

tuple(set_color[:3])

650

)

651

except:

652

# Fallback if threshold not available

653

result_surface = original.copy()

654

num_matches = 0

655

656

screen.fill((50, 50, 50))

657

658

# Draw original and result

659

screen.blit(original, (50, 50))

660

screen.blit(result_surface, (400, 50))

661

662

# Labels and info

663

font = pygame.font.Font(None, 36)

664

665

original_label = font.render("Original", True, (255, 255, 255))

666

screen.blit(original_label, (50, 10))

667

668

result_label = font.render("Threshold Result", True, (255, 255, 255))

669

screen.blit(result_label, (400, 10))

670

671

# Show threshold color

672

threshold_rect = pygame.Rect(50, 300, 50, 50)

673

pygame.draw.rect(screen, tuple(threshold_color[:3]), threshold_rect)

674

675

threshold_info = font.render(f"Threshold Color: {threshold_color[:3]}", True, (255, 255, 255))

676

screen.blit(threshold_info, (120, 310))

677

678

matches_info = font.render(f"Matching pixels: {num_matches}", True, (255, 255, 255))

679

screen.blit(matches_info, (50, 370))

680

681

instructions = [

682

"Press R to randomize red component",

683

"Press G to randomize green component",

684

"Press B to randomize blue component"

685

]

686

687

for i, instruction in enumerate(instructions):

688

text = font.render(instruction, True, (255, 255, 255))

689

screen.blit(text, (50, 450 + i * 30))

690

691

pygame.display.flip()

692

clock.tick(60)

693

694

pygame.quit()

695

```