Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Showcase / Scaling and Blurring images in TexPlay
- By banister Date 2009-09-26 05:55
I was talking to Snae in #gosu and he wanted to scale a texture (during a splice) and also to blur. Unfortunately TexPlay does not natively support this functionality. However, we can build it in easily enough and even give it a pretty interface using the macro functionality.

This is the API we came up with:

To scale an image (during a splice):

img1.splice_and_scale img2, x, y, :factor => 2

* The above will splice a scaled version of img2 into img1
* Note x and y coords can be scaled separately using :factor_x and :factor_y.
* Valid scale factors include all positive integers and fractional values. Including values < 1 for minimization.

To blur an image:

img1.blur :blur_radius => 4

* Set the blur radius (degree of blurriness) using the :blur_radius parameter
* Can also specify a region to blur using the :region parameter, a la the TexPlay#each iterator
* Note: blurring is SLOW, so do not use for real time stuff. Just use for pre-rendering images
* A much faster version of this blurring algorithm will be available soon

Here are the implementations of the above functions:

# Scaling
# uses nearest-neighbour
TexPlay::create_macro(:splice_and_scale) do |img, cx, cy, *options|
    options = options.first ? options.first : {}

    options = {
        :color_control => proc do |c1, c2, x, y|
            factor = options[:factor] || 1
            factor_x = options[:factor_x] || factor
            factor_y = options[:factor_y] || factor

            x = factor_x * (x - cx) + cx
            y = factor_y * (y - cy) + cy
           
            rect x, y, x + factor_x, y + factor_y, :color => c2, :fill => true
            :none
        end
    }.merge!(options)
   
    splice img, cx, cy, options

    self
end

# Blurring
# Code by Snae
TexPlay::create_macro(:blur) do |*options|
    options = options.first ? options.first : {}
   
    radius = options[:blur_radius] || 1
   
    self.each(options) { |c,x,y|
        total = [0, 0, 0, 0]
        for ky in (-radius..radius)
            for kx in (-radius..radius)
                v = get_pixel(x + kx, y + ky);
                if v
                    total[0] += v[0]
                    total[1] += v[1]
                    total[2] += v[2]
                    total[3] += v[3]
                end
            end
        end

        c[0] = total[0] / (radius * 2 + 1) ** 2
        c[1] = total[1] / (radius * 2 + 1) ** 2
        c[2] = total[2] / (radius * 2 + 1) ** 2
        c[3] = total[3] / (radius * 2 + 1) ** 2
    }
   
    self
end

attached are some examples of scaling / blurring images. Note that the blurring example only blurs a portion of the image (the area where the people are walking)
- By devgomes Date 2009-09-26 16:15 Edited 2009-09-27 15:53
Hey, Snae here.

I did some improvements that decreased the time to blur the Gosu logo to 1.5 seconds on my computer.
Don't forget that the calculations for blurring are all in Ruby (1.8) and that there would be a significant increase of performance in C.
You can also make it just a vertical blur or horizontal blur since the algorithm is split on those two steps, the slowest one is the vertical step since I can't use the horizontal step algorithm using texplay's "each" (I need it to go outer loop left -> right and inner loop top -> bottom on the 2d pixel array).

        total = [0, 0, 0, 0]
        @logo_image.each { |c,x,y|
          if (x == 0)
            total = [0, 0, 0, 0]
            for kx in (-radius..radius)
              v = @logo_image.get_pixel(kx, y);
              if v
                total[0] += v[0]
                total[1] += v[1]
                total[2] += v[2]
                total[3] += v[3]
              end
            end
            c[0] = total[0] / (radius * 2 + 1)
            c[1] = total[1] / (radius * 2 + 1)
            c[2] = total[2] / (radius * 2 + 1)
            c[3] = total[3] / (radius * 2 + 1)
          else
            b = @logo_image.get_pixel(x - radius - 1, y);
            if b
              total[0] -= b[0]
              total[1] -= b[1]
              total[2] -= b[2]
              total[3] -= b[3]
            end
            n = @logo_image.get_pixel(x + radius, y);
            if n
              total[0] += n[0]
              total[1] += n[1]
              total[2] += n[2]
              total[3] += n[3]
            end
           
            c[0] = total[0] / (radius * 2 + 1)
            c[1] = total[1] / (radius * 2 + 1)
            c[2] = total[2] / (radius * 2 + 1)
            c[3] = total[3] / (radius * 2 + 1)
          end
        }
     
        @logo_image.each { |c,x,y|
          total = [0, 0, 0, 0]
          for ky in (-radius..radius)
            v = @logo_image.get_pixel(x, y + ky);
            if v
              total[0] += v[0]
              total[1] += v[1]
              total[2] += v[2]
              total[3] += v[3]
            end
          end
         
          c[0] = total[0] / (radius * 2 + 1)
          c[1] = total[1] / (radius * 2 + 1)
          c[2] = total[2] / (radius * 2 + 1)
          c[3] = total[3] / (radius * 2 + 1)
        }

The code looks big, doesn't it? :)
I don't know if a transpose algorithm would help performance or not but it would decrease the code size a little.
Attachment: gosublur.zip (0B)
- By RunnerPack Date 2009-09-27 09:59
Hey, banister (and Snae ;),

Very nice! I can definitely think of ideas for these. I had some ideas for when/if you convert them to C(++) code.

First, how about incorporating the Hq2x, 3x, and 4x filters from the links at the bottom of this Wikipedia article: http://en.wikipedia.org/wiki/Hqx (they're webarchived, but the links to the source zips still seem to work) as well? Not as part of the normal scale, just as additional options. They should be real-time or pretty close to it on any PC that can run Gosu. BTW, the Hq3x page explains the algorithm.

Another idea would be to make methods that scale or blur an Image (without having to splice it) and return a new Image object.

kthxbye
- By banister Date 2009-09-27 11:21
hey RunnerPack, ltns :)

Yeah I think the blurring algorithm is a good candidate for a C rewrite, however the scaling is actually reasonably fast in ruby (at least the current nearest neighbour method).

Regarding your last point: "make methods that scale...an Image (without having to splice it) and return a new Image object.". That's actually really easy to do at the moment. Should just be a matter of creating a blank image (of appropriate dimensions) using TexPlay::create_blank_image(), and then using splice_and_scale(image, 0, 0, :factor => size) into the blank image.

i hope to put a whole bunch of 'effects' into the 0.3.0 release of texplay.

:)
- By erisdiscord Date 2009-09-27 16:57
Why not just have the scale factor as an optional parameter to the splice method? I think it would make for a cleaner API.
Up Topic Gosu / Gosu Showcase / Scaling and Blurring images in TexPlay

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill