Created
April 18, 2017 07:06
-
-
Save mieki256/56ba860526f2bf88baa45360ad5f2b1c to your computer and use it in GitHub Desktop.
Ruby+cairo(rcairo)+DXRubyでドット絵モドキを自動生成。動作にはRuby、cairo(rcairo)、color、DXRubyが必要。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
screenshot.