Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

Bitmap Resizer with anti-aliasing

Bitmap Resizer Version: 1.0
By: Krän

Introduction

This script allows to resize bitmaps with anti-aliasing.
It can only reduce pictures, and is super-duper slow.

Krän made this script for me long time ago. I'm looking for some help (or another script which can do the same thing) to improve this script.




Screenshots



<- Original picture
-> resized with RM
---> resized with the script



Script



Code:
 

 

 

class Bitmap

 

 

  def resize(zoom)

    big_bmp = Bitmap.new(self.width*zoom, self.height*zoom)

    big_bmp.stretch_blt(big_bmp.rect, self, self.rect)

    return big_bmp

  end

 

 

  def resize_aa(zoom)

    bmp = self.zoom_aa_x(zoom)

    bmp = bmp.zoom_aa_y(zoom)

    return bmp

  end

 

 

 

  #--------------------------------------------------------------------------------------------------------------------------------

  # - Nom : Script de redimensionnement avec Anti-Aliasing

  # - Auteur : Krän

  # - But : Réduire une image en adoussissant les contours.(Moins de crénelage)

  # - Utilisation : Utilisez la fonction zoom_AA_x(zoom) ou zoom_AA_y(zoom)

  #   sur un bitmap pour que cette fonction renvoie un autre bitmap dont la hauteur ou la largeur

  #   ont changé. L'argument zoom correspond au rapport entre la nouvelle dimension sur l'acienne.

  # - Exemple : bitmap.zoom_AA_x(0.5) renverra la même image que bitmap dont la largeur la

  #   moitié de bitmap.

  #--------------------------------------------------------------------------------------------------------------------------------

  #

  # Attention : pour le moment ce script ne permet que de réduire des images. L'argument zoom

  # doit être inférieur à 1

  #

 

 

  def zoom_aa_x(zoom)

    nouvelle_taille = zoom * self.width

    nouveau_bitmap = Bitmap.new(nouvelle_taille,self.height)

 

    taille_section = self.width.to_f / nouvelle_taille.to_f

 

    #On analyse le nouveau bitmap dans sa largeur :

    for x in 0..nouvelle_taille

      #On établit où se trouve le curseur sur l'image d'origine :

      curseur = x.to_f * taille_section

 

      #On détermine la section de l'image d'origine correspondant à un pixel de la nouvelle :

      debut = curseur.to_int

      fin = (curseur + taille_section).to_int

      #On détermine les rapports de debut et fin de section (On détermine l'importance des pixels extrèmes) :

      rapport1 = (curseur.to_int + 1 - curseur)

      rapport2= (curseur + taille_section) - (curseur + taille_section).to_int

 

      for y in 0..self.height

        #On crée 4 variables correspondant aux nouvelle couleurs du pixel :

        red = 0.0

        green = 0.0

        blue = 0.0

        alpha = 0.0

 

        for i in debut..fin

          if i == debut

            red += self.get_pixel(i,y).red * rapport1

            green += self.get_pixel(i,y).green * rapport1

            blue += self.get_pixel(i,y).blue * rapport1

            alpha += self.get_pixel(i,y).alpha * rapport1

          elsif  i == fin

            red += self.get_pixel(i,y).red * rapport2

            green += self.get_pixel(i,y).green * rapport2

            blue += self.get_pixel(i,y).blue * rapport2

            alpha += self.get_pixel(i,y).alpha * rapport2

          else

            red += self.get_pixel(i,y).red

            green += self.get_pixel(i,y).green

            blue += self.get_pixel(i,y).blue

            alpha += self.get_pixel(i,y).alpha

          end

        end

        #On fait les moyennes

        red /= taille_section

        green /= taille_section

        blue /= taille_section

        alpha /= taille_section#taille_section

 

        #On donne une couleur au nouveau pixel :

        nouveau_bitmap.set_pixel(x, y, Color.new(red,green,blue,alpha))

      end

    end

    return nouveau_bitmap

  end

 

 

 

  def zoom_aa_y(zoom)

    nouvelle_taille = zoom * self.height

    nouveau_bitmap = Bitmap.new(self.width,nouvelle_taille)

 

    taille_section = self.height.to_f / nouvelle_taille.to_f

 

    #On analyse le nouveau bitmap dans sa largeur :

    for y in 0..nouvelle_taille

      #On établit où se trouve le curseur sur l'image d'origine :

      curseur = y.to_f * taille_section

 

      #On détermine la section de l'image d'origine correspondant à un pixel de la nouvelle :

      debut = curseur.to_int

      fin = (curseur + taille_section).to_int

      #On détermine les rapports de debut et fin de section (On détermine l'importance des pixels extrèmes) :

      rapport1 = (curseur.to_int + 1 - curseur)

      rapport2= (curseur + taille_section) - (curseur + taille_section).to_int

 

      for x in 0..self.width

        #On crée 4 variables correspondant aux nouvelle couleurs du pixel :

        red = 0.0

        green = 0.0

        blue = 0.0

        alpha = 0.0

 

        for i in debut..fin

          if i == debut

            red += self.get_pixel(x,i).red * rapport1

            green += self.get_pixel(x,i).green * rapport1

            blue += self.get_pixel(x,i).blue * rapport1

            alpha += self.get_pixel(x,i).alpha * rapport1

          elsif  i == fin

            red += self.get_pixel(x,i).red * rapport2

            green += self.get_pixel(x,i).green * rapport2

            blue += self.get_pixel(x,i).blue * rapport2

            alpha += self.get_pixel(x,i).alpha * rapport2

          else

            red += self.get_pixel(x,i).red

            green += self.get_pixel(x,i).green

            blue += self.get_pixel(x,i).blue

            alpha += self.get_pixel(x,i).alpha

          end

        end

        #On fait les moyennes

        red /= taille_section

        green /= taille_section

        blue /= taille_section

        alpha /= taille_section#taille_section

 

        #On donne une couleur au nouveau pixel :

        nouveau_bitmap.set_pixel(x, y, Color.new(red,green,blue,alpha))

      end

    end

    return nouveau_bitmap

  end

  

 

end

Instructions

Juste use the methods resize and resize_aa on an existing bitmap to obtain the resized one.


Compatibility

RMXP compatible.


Credits and Thanks

- Krän
- the guy who drawn this nice pikachu :)
 
Quite a nice script !
Why separate operations on the width and length? You lose a lot of time on this. You should find a way to redraw x and y pixels at once. And regardless of the width/height ratio.

Pourquoi séparer les opérations sur la largeur et sur la longueur ? On perd pas mal de temps avec ca. Il faudrait trouver le moyen de traiter les deux à la fois, quel que soit le ratio h/l

regards,

berka
 
You should find
Mathematics functions are really not what I'm good at. It's why I asked Krän to develop this script, and it's why I can't improve it.

So, I just added a function to use both operations. I asked Krän if he can work again on this script. I'm waiting for his answer.

And I was hoping this function already exists... it would be curious if it not exists, because it's super usefull.
 
berka":3cmhb1o8 said:
Why separate operations on the width and length? You lose a lot of time on this.

Not true. Imagine a 3x3 (9 pixels) image that you are reducing to 1 pixel. To compare each pixel to all of it's adjacent pixels, you would have the following number of comparisons:

  • -------
    |3|5|3|
    -------
    |5|8|5|
    -------
    |3|5|3|
    ------- 40 comparisons

Now, doing only one direction at a time, you have

  • -------
    |1|1|1|
    -------
    |2|2|2|
    -------
    |1|1|1|
    ------- 12 comparisons

    then

    -------
    |1|2|1|
    ------- 4 comparisons

    for a total of 16 to achieve the same results.

As your image increases in size, the difference should approach 1/2.
So it's still theoretically twice as efficient.
 
Hi King, how are you doing :)
I got one simple option although I am not sure rmxp is meant to do such a thing in a fast manner

Well, first you could try using a table for your bitmap and replace the get_pixel by a simple table test. Table test is much faster than bitmap.get_pixel, that may improve your code
You would them need method to create, read and write on your table:
In Module RPG::Cache: for instance

Code:
    @tables = {}

    

    #---------------------------------------------------------------------------

    # creates a table

    #---------------------------------------------------------------------------

    def self.init_table(folder_name, filename)

      path = folder_name + filename

      return if (@cache[path] == nil)

      @tables[@cache[path]] = Table.new(@cache[path].width, @cache[path].height)

    end

    

    #---------------------------------------------------------------------------

    # reads a table

    #---------------------------------------------------------------------------

    def self.read_table(bitmap, x, y)

      return 0 if (@tables[bitmap] == nil)

      return @tables[bitmap][x, y]

    end

    

    #---------------------------------------------------------------------------

    # writes into a table

    #---------------------------------------------------------------------------

    def self.write_table(bitmap, x, y, value)

      if (@tables[bitmap] == nil)

        @tables[bitmap] = Table.new(bitmap.width, bitmap.height)

      end

      @tables[bitmap][x, y] = value

      return @tables[bitmap][x, y]

    end

Now back on my 68000 reflexes you could also avoid some multiplication. Well I am not sure it would have much an impact on a language lile ruby, but it may give you some idea so here's my proposal
Your color range is simple : 0..255
So no matter your "rappport" variable you will always get a 0.255 value in the end
So in 68000 we used to have multplication table. Here it would be a 256*256 table when, say, x=color value and y=rapport value
So now, you just have to turn "rapport" in a 0.255 value (very simple just * 255 and make it an integer)
Then instead of:
return color.r*rapport
you use you premultiplied table: return premult(color.r, rapportInterger)

Again, I am not sure it would have a significant impact in ruby, I dont know how much faster a 2dtable is against a multiplication
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top