crimage

A comprehensive 2D image processing library for Crystal with support for multiple formats (PNG, JPEG, GIF, BMP, TIFF, WebP, ICO), drawing primitives, text rendering with TrueType fonts, color transformations, filters, and effects. Includes SIMD-accelerated operations for performance-critical tasks. bmp font-rendering ico image-analysis image-drawer image-effects image-filters image-processing image-transformations jpeg jpg png tiff webp crystal-shard
HEAD Latest release released
naqvis/crimage
22 1
Ali Naqvi

CrImage

A comprehensive pure-Crystal image processing library with no external dependencies.

Why CrImage?

A few highlights:

  • Complete image toolkit: formats, drawing, text, filters, effects — all in one shard
  • Built-in TrueType font engine (no FreeType dependency)
  • Animated GIF creation and multi-resolution ICO support
  • QR code generation, blurhash encoding, smart cropping
  • Decompression bomb protection for untrusted images

See Features at a Glance for the full list.

Installation

Add to your shard.yml:

dependencies:
  crimage:
    github: naqvis/crimage

Then run shards install.

Quick Start

require "crimage"

# Read any supported format (auto-detected)
img = CrImage.read("photo.jpg")

# Basic operations
resized = img.resize(800, 600, method: :lanczos)
thumbnail = img.thumb(200)  # 200x200 square

# Apply filters
result = img.brightness(20).contrast(1.2).blur(2)

# Save in any format
CrImage::PNG.write("output.png", result)
CrImage::WEBP.write("output.webp", result)

Features at a Glance

| Category | Capabilities | | ------------- | -------------------------------------------------------------------- | | Formats | PNG, JPEG, GIF (animated), BMP, TIFF, WebP (VP8L), ICO | | Color | RGBA, NRGBA, Gray, CMYK, YCbCr, HSV, HSL, LAB, Paletted | | Transform | Resize (nearest/bilinear/bicubic/lanczos), rotate, crop, flip | | Filters | Blur, sharpen, edge detection (Sobel/Prewitt/Roberts/Scharr) | | Effects | Sepia, emboss, vignette, temperature, grayscale, invert | | Analysis | Histogram, CLAHE, SSIM, PSNR, perceptual hash, palette extraction | | Drawing | Lines, circles, polygons, gradients, Bézier, patterns, chart helpers | | Text | TrueType/OpenType/WOFF fonts, multi-line, alignment, effects | | Utilities | Thumbnails, watermarks, sprites, QR codes, blurhash, smart crop |

Common Tasks

Resize and Thumbnails
# Fit within bounds (preserves aspect ratio)
fitted = img.fit(800, 600)

# Fill exact dimensions (crops excess)
filled = img.fill(800, 600)

# Square thumbnail
avatar = img.thumb(200)

# High-quality resize
hq = img.resize(1920, 1080, method: :lanczos)
Color Adjustments
# Basic adjustments
brighter = img.brightness(30)      # -255 to 255
contrasted = img.contrast(1.5)     # 0.0 to 2.0
grayscale = img.grayscale
inverted = img.invert

# Effects
vintage = img.sepia
warm = img.temperature(30)         # positive = warmer
cool = img.temperature(-30)        # negative = cooler
Filters
# Blur
blurred = img.blur(radius: 3)
gaussian = img.blur_gaussian(radius: 5, sigma: 2.0)

# Sharpen
sharp = img.sharpen(amount: 1.5)

# Edge detection
edges = img.sobel
edges = img.prewitt
edges = img.detect_edges(threshold: 50)
Drawing
img = CrImage.rgba(400, 400, CrImage::Color::WHITE)

# Shapes
img.draw_line(0, 0, 400, 400, color: CrImage::Color::RED)
img.draw_circle(200, 200, 50, color: CrImage::Color::BLUE, fill: true)
img.draw_rect(50, 50, 100, 80, stroke: CrImage::Color::BLACK)

# Thick anti-aliased lines
style = CrImage::Draw::LineStyle.new(CrImage::Color::RED, thickness: 5, anti_alias: true)
CrImage::Draw.line(img, CrImage::Point.new(50, 50), CrImage::Point.new(350, 350), style)

# Ring slice (for donut charts)
ring_style = CrImage::Draw::RingStyle.new(CrImage::Color::BLUE, fill: true)
CrImage::Draw.ring_slice(img, CrImage::Point.new(200, 200), 40, 80, 0.0, Math::PI, ring_style)

# Clipping regions
img.with_clip(50, 50, 200, 200) do |clipped|
  # Drawing here is restricted to the clip region
  clipped.draw_circle(100, 100, 150, color: CrImage::Color::GREEN, fill: true)
end

# Gradients
stops = [
  CrImage::Draw::ColorStop.new(0.0, CrImage::Color::RED),
  CrImage::Draw::ColorStop.new(1.0, CrImage::Color::BLUE)
]
gradient = CrImage::Draw::LinearGradient.new(
  CrImage::Point.new(0, 0),
  CrImage::Point.new(400, 0),
  stops
)
CrImage::Draw.fill_linear_gradient(img, img.bounds, gradient)
Text Rendering
require "freetype"

font = FreeType::TrueType.load("path/to/font.ttf")
face = FreeType::TrueType.new_face(font, 48.0)

img = CrImage.rgba(400, 100, CrImage::Color::WHITE)
text_color = CrImage::Uniform.new(CrImage::Color::BLACK)
drawer = CrImage::Font::Drawer.new(img, text_color, face)

# Simple text
drawer.draw_text("Hello, World!", 20, 60)

# With effects
drawer.draw_text("Styled", 20, 60,
  shadow: true,
  outline: true,
  underline: true
)

# Easy text measurement
width = face.measure("Hello")
width, height = face.text_size("Hello")
line_height = face.line_height
Animated GIFs
frames = (0...10).map do |i|
  frame_img = CrImage.rgba(100, 100, CrImage::Color::WHITE)
  frame_img.draw_circle(10 + i * 8, 50, 10, color: CrImage::Color::RED, fill: true)
  CrImage::GIF::Frame.new(frame_img, delay: 10)  # 100ms per frame
end

animation = CrImage::GIF::Animation.new(frames, 100, 100, loop_count: 0)
CrImage::GIF.write_animation("animated.gif", animation)
QR Codes
# Simple
qr = CrImage.qr_code("https://example.com")
CrImage::PNG.write("qr.png", qr)

# With options
qr = CrImage.qr_code("Hello",
  size: 400,
  error_correction: :high,
  margin: 4
)
Blurhash
# Encode image to blurhash string
hash = img.to_blurhash  # => "LKO2?U%2Tw=w]~RBVZRi};RPxuwH"

# Decode to placeholder
placeholder = CrImage::Util::Blurhash.decode(hash, width: 32, height: 32)
Image Comparison
original = CrImage.read("original.png")
modified = CrImage.read("modified.png")

mse = original.mse(modified)        # Mean Squared Error
psnr = original.psnr(modified)      # Peak Signal-to-Noise Ratio (dB)
ssim = original.ssim(modified)      # Structural Similarity (0-1)

# Visual diff for testing
diff = original.visual_diff(modified)
CrImage::PNG.write("diff.png", diff)

Font Support

The library includes a complete TrueType font engine:

| Feature | Status | | ------------------------------- | ------------------------------------- | | TrueType (.ttf) | ✅ Full support | | OpenType/CFF (.otf) | ✅ Full support | | WOFF (.woff) | ✅ Auto-decompression | | TrueType Collections (.ttc) | ✅ Multi-font files | | Legacy kern table | ✅ Pair kerning | | GPOS kerning | ✅ Modern pair positioning | | GSUB ligatures | ✅ Standard ligatures (fi/fl/ffi/ffl) | | Vertical text | ✅ Top-to-bottom layout | | Variable fonts | ⚠️ Axis detection only | | Complex scripts (Arabic, Indic) | ❌ Requires external shaper |

Note: Text rendering uses left-to-right glyph placement. Complex scripts (Arabic, Hebrew, Thai, Indic) and advanced OpenType features (contextual forms, mark positioning) require an external shaping engine like HarfBuzz.

Architecture

CrImage::Image          # Base interface for all image types
├── CrImage::RGBA       # 8-bit RGBA (premultiplied alpha)
├── CrImage::NRGBA      # 8-bit RGBA (non-premultiplied)
├── CrImage::Gray       # 8-bit grayscale
├── CrImage::Alpha      # 8-bit alpha channel
├── CrImage::CMYK       # Print color model
├── CrImage::YCbCr      # JPEG color model
├── CrImage::Paletted   # Indexed color (GIF)
└── CrImage::Uniform    # Solid color fill

CrImage::Draw           # Drawing primitives
CrImage::Transform      # Resize, rotate, filters
CrImage::Util           # Thumbnails, watermarks, etc.
CrImage::Font           # Text rendering
FreeType::TrueType      # Font parsing

Documentation

| Guide | Description | | -------------------------------------------------- | ---------------------------------------- | | Quick Start | 10 common tasks with examples | | Image Formats | Supported formats and adding custom ones | | Drawing & Text | Primitives, gradients, fonts | | Transforms & Filters | Resize, rotate, blur, effects | | Color Models | RGBA, HSV, LAB conversions | | Utilities | Thumbnails, watermarks, QR, blurhash | | Performance | Optimization and thread-safety | | Security | Handling untrusted images |

Examples: See examples/ for 60+ working demos.

API Docs: Run crystal docs and open docs/index.html.

Development

crystal spec          # Run tests
crystal docs          # Generate API docs

Contributing

  1. Fork it (https://github.com/naqvis/crimage/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

License

MIT License. See LICENSE for details.

Contributors

crimage:
  github: naqvis/crimage
  
License MIT
Crystal ~> 1.0

Authors

Dependencies 0

Development Dependencies 0

Dependents 1

Last synced .
search fire star recently