Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Ruby+cairo(rcairo)+DXRubyでドット絵モドキを自動生成。動作にはRuby、cairo(rcairo)、color、DXRubyが必要。
#!ruby -Ku
# -*- mode: ruby; coding: utf-8 -*-
# Last updated: <2017/04/18 15:50:22 +0900>
#
# tinypixelartgen_take4.rb
#
# generate pixelart take4
#
# usage: ruby tinypixelartgrad.rb
#
# space key : new generate
#
# Runtime Dependencies
# cairo >= 1.15.5
# color >= 1.8
# dxruby >= 1.4.5
#
# Author : mieki256
# License : CC0 / Public Domain
require 'cairo'
require 'stringio'
require 'color'
class TinyPixelartGrad
# @return [ImageSurface] Cairo::ImageSurface
attr_accessor :surface
# @return [true, false] x mirror
attr_accessor :x_mirror
# @return [true, false] y mirror
attr_accessor :y_mirror
# initialize
# @param w [Integer] width
# @param h [Integer] height
# @param seed [Integer] random seed
# @param x_mirror [true, false] x mirror
# @param y_mirror [true, false] y mirror
# @param border_width [Float] border width. default 1.0
# @param border_alpha [Float] border alpha. default 1.0
# @param hue [Integer] hue. 0-360
def initialize(w, h, seed, x_mirror: nil, y_mirror: nil,
border_width: 1.0, border_alpha: 1.0, hue: nil)
srand(seed)
@x_mirror = (x_mirror == nil)? ((rand > 0.5)? true : false) : x_mirror
@y_mirror = (y_mirror == nil)? ((rand > 0.5)? true : false) : y_mirror
@border_width = border_width
@border_alpha = border_alpha
@hue_base = (hue == nil)? (rand * 360) : hue
fmt = Cairo::FORMAT_ARGB32
surface = Cairo::ImageSurface.new(fmt, w, h)
Cairo::Context.new(surface) { |ctx|
# ctx.set_antialias(Cairo::Antialias::GRAY)
ctx.set_antialias(Cairo::Antialias::NONE)
ctx.set_line_width(@border_width)
ctx.set_line_join(Cairo::LineJoin::MITER)
ctx.set_line_cap(Cairo::LineCap::SQUARE)
cnt = rand(32) + 16
cnt.times do |i|
hue = (@hue_base + rand(45)) % 360
sat = 10 + rand(90)
lum = 30 + rand(70)
wh, hh = (w * 0.8).to_i, (h * 0.8).to_i
rx = (rand(wh) + rand(wh) + rand(wh)) / 3
ry = (rand(hh) + rand(hh) + rand(hh)) / 3
if rand < 0.8
# draw rectangle
rw = rand((w - rx) / 2) + 1
rh = rand((h - ry) / 2) + 1
draw_rect(ctx, rx, ry, rw, rh, hue, sat, lum)
else
# draw circle
mm = [rx, ry, (w - rx), (h - ry)].min
rr = rand((mm * 0.4).to_i) + 2
draw_circle(ctx, rx, ry, rr, hue, sat, lum)
end
end
}
surface = mirror_x(surface) if @x_mirror
surface = mirror_y(surface) if @y_mirror
@surface = surface
end
# get linear gradation pattern
# @param x [Integer] x
# @param y [Integer] y
# @param w [Integer] width
# @param h [Integer] height
# @param hue [Integer] hue. 0-360
# @param sat [Integer] saturation. 0-100
# @param lum [Integer] luminance. 0-100
# @param hor [true, false] horizontal gradation
# @param rpt [true, false] repeat gradation
def get_linear_pattern(x, y, w, h, hue, sat, lum, hor, rpt)
l1 = lum
l0 = [(l1 - (rand(30) + 20)), 0].max
l2 = [(l1 + (rand(30) + 20)), 100.0].min
rgb0 = Color::HSL.new(hue, sat, l0).to_rgb
rgb1 = Color::HSL.new(hue, sat, l1).to_rgb
rgb2 = Color::HSL.new(hue, sat, l2).to_rgb
# rgb.red : return 0 - 255
# rgb.r : return 0.0 - 1.0
if hor
# horizontal gradation
lg = Cairo::LinearPattern.new(x, y, x + w, y)
else
# vertical gradation
lg = Cairo::LinearPattern.new(x, y, x, y + h)
end
if rpt
# repeat gradation
lg.add_color_stop_rgb(0.0, rgb0.r, rgb0.g, rgb0.b)
lg.add_color_stop_rgb(0.2, rgb1.r, rgb1.g, rgb1.b)
lg.add_color_stop_rgb(0.5, rgb2.r, rgb2.g, rgb2.b)
lg.add_color_stop_rgb(0.8, rgb1.r, rgb1.g, rgb1.b)
lg.add_color_stop_rgb(1.0, rgb0.r, rgb0.g, rgb0.b)
else
# single gradation
lg.add_color_stop_rgb(0.0, rgb0.r, rgb0.g, rgb0.b)
lg.add_color_stop_rgb(0.5, rgb1.r, rgb1.g, rgb1.b)
lg.add_color_stop_rgb(1.0, rgb2.r, rgb2.g, rgb2.b)
end
return lg
end
# draw rectangle
def draw_rect(ctx, x, y, w, h, hue, sat, lum)
hor = (rand < 0.5)? true : false
rpt = (rand < 0.3)? true : false
if w <= 2 or h <= 2
# no border
lg = get_linear_pattern(x, y, w, h, hue, sat, lum, hor, rpt)
ctx.set_source(lg)
ctx.rectangle(x, y, w, h)
ctx.fill
else
# with border
lg = get_linear_pattern(x, y, w, h, hue, sat, lum, hor, rpt)
ctx.set_source(lg)
ra = ([w, h].min) / 3
if ra <= 1 or rand < 0.5
# rectangle
ctx.rectangle(x, y, w, h)
else
# rounded rectangle
ctx.rounded_rectangle(x, y, w, h, ra)
end
ctx.fill_preserve
ctx.set_source_rgba(0, 0, 0, @border_alpha)
ctx.stroke
end
end
# draw circle
def draw_circle(ctx, x, y, r, hue, sat, lum)
l1 = lum
la = rand(30) + 20
l0 = [(l1 - la), 0].max
l2 = [(l1 + la), 100].min
rgb0 = Color::HSL.new(hue, sat, l0).to_rgb
rgb1 = Color::HSL.new(hue, sat, l1).to_rgb
rgb2 = Color::HSL.new(hue, sat, l2).to_rgb
rg = Cairo::RadialPattern.new(x, y, 0, x, y, r)
rg.add_color_stop_rgb(1.0, rgb0.r, rgb0.g, rgb0.b)
rg.add_color_stop_rgb(0.7, rgb1.r, rgb1.g, rgb1.b)
rg.add_color_stop_rgb(0.0, rgb2.r, rgb2.g, rgb2.b)
ctx.set_source(rg)
ctx.arc(x, y, r, 0, 2 * Math::PI)
ctx.fill_preserve
ctx.set_source_rgba(0, 0, 0, @border_alpha)
ctx.stroke
end
# cairo surface mirror x
# @param surface [ImageSurface] cairo ImageSurface
# @return [ImageSurface] x mirror cairo ImageSurface
def mirror_x(surface)
w, h = surface.width, surface.height
src = surface.data.unpack("L*")
xx = (w / 2.0).round
x = 0
x1 = w - 1
while x < xx
h.times do |y|
src[y * w + x1] = src[y * w + x]
end
x += 1
x1 -= 1
end
dst = src.pack("L*")
fmt = Cairo::FORMAT_ARGB32
return Cairo::ImageSurface.new(dst, fmt, w, h, w * 4)
end
# cairo surface mirror y
# @param surface [ImageSurface] cairo ImageSurface
# @return [ImageSurface] y mirror cairo ImageSurface
def mirror_y(surface)
w, h = surface.width, surface.height
src = surface.data.unpack("L*")
yy = (h / 2.0).round
y = 0
y1 = h - 1
while y < yy
w.times do |x|
src[y1 * w + x] = src[y * w + x]
end
y += 1
y1 -= 1
end
dst = src.pack("L*")
fmt = Cairo::FORMAT_ARGB32
return Cairo::ImageSurface.new(dst, fmt, w, h, w * 4)
end
end
if $0 == __FILE__
# ----------------------------------------
require 'dxruby'
# convert cairo surface to DXRuby Image
# @param w [Integer] width
# @param h [Integer] height
# @param surface [ImageSurface] cairo ImageSurface
# @return [Image] DXRuby Image
def convert_image(w, h, surface)
temp = StringIO.new("", 'w+')
surface.write_to_png(temp)
temp.rewind
img = Image.loadFromFileInMemory(temp.read)
temp.close
return img
end
# generate many pixelart
# @param w [Integer] width
# @param h [Integer] height
# @param count [Integer] number
# @param seed [Integer] random seed
# @return [Array<Image>] many DXRuby Image
def generate_arts(w, h, count, seed)
srand(seed)
imgs = []
count.times do |i|
art = TinyPixelartGrad.new(w, h, seed + i)
img = convert_image(w, h, art.surface)
imgs.push(img)
end
return imgs
end
wdw_w, wdw_h = 640, 480
Window.resize(wdw_w, wdw_h)
Window.bgcolor = [32, 96, 128]
# Window.scale = 2
w = 64
h = w
seed = 0
count = (wdw_w / (w + 4)) * (wdw_h / (h + 4))
imgs = generate_arts(w, h, count, seed)
seed += count
Window.loop do
break if Input.keyPush?(K_ESCAPE)
if Input.keyPush?(K_SPACE)
imgs = generate_arts(w, h, count, seed)
seed += count
end
x, y = 0, 0
imgs.each do |img|
Window.draw(x, y, img)
x += img.width + 4
if x + img.width >= Window.width
y += img.height + 4
x = 0
end
end
end
end
@mieki256
Copy link
Author

mieki256 commented Apr 18, 2017

screenshot.

tinypixelartgrad_ss

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment