61f1b34f172ee1fbc1af42ab3ce0eca9

I need to reformat .bmp data, by adding a lot of '0'
But my code is... horribly ugly.
'data' contain the original data (array)
'corrected' is an empty array

Before process :
[XXXX
XXXX
XXXX]

After process :
[XXXX0000
XXXX0000
XXXX0000
00000000
00000000
00000000]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
for y in 0...height
  for x in 0...width
    # BGR => RGB & miror y
    z = (height-y-1)*width*3 + 3*x
    corrected << data[z+2]
    corrected << data[z+1]
    corrected << data[z  ]
    corrected << 255 # Add alpha value
  end
  for x in width...real_size
    corrected << 0
    corrected << 0
    corrected << 0
    corrected << 0
  end
end
for y in height...real_size
  for x in 0...real_size
    corrected << 0
    corrected << 0
    corrected << 0
    corrected << 0
  end
end

Refactorings

No refactoring yet !

D85d44a0eca045f40e5a31449277c26c

Ben Marini

January 31, 2010, January 31, 2010 20:33, permalink

No rating. Login to rate!

I'd like to help out, but I can't quite figure out what your code snippet is doing. Can you provide a more complete code snippet? For instance, define `height`, `width`, `real_size`, `data`, and `corrected`?

61f1b34f172ee1fbc1af42ab3ce0eca9

darkleo.myopenid.com

January 31, 2010, January 31, 2010 21:36, permalink

No rating. Login to rate!

I read `data`, `width` and `height` directly from a bmp file.
To use it, OpenGL requires that the image size (on each dimension) be a power of 2.
So `real_size` is the closest power of 2 who match the image size.
The original data is encoded in BGR, and i need it in RGBA.
Finally i need to reverse the rows, because the data "begin from the end"

I give an example with a very simple bitmap, 3 rows, 2 columns :
Red/Red
Green/Green
Blue/Blue

With this 3*2 bitmap :
data = [ 255, 0, 0, # 1st pixel, blue (third row)
255, 0, 0, # 2st pixel, blue
0, 255, 0, # 3nd pixel, green (second row)
0, 255, 0, # 4nd pixel, green
0, 0, 255, # 5rd pixel, red (first row)
0, 0, 255, # 6rd pixel, red
]
width = 2
height = 3

We calculate :
real_size = 4 # closest power of 2

And we obtain a 4*4 bitmap :
corrected = [ 255, 0, 0, 255 # 1st pixel, red (first row)
255, 0, 0, 255 # 2st pixel, red
0, 0, 0, 0 # Empty
0, 0, 0, 0 # Empty
0, 255, 0, 255 # 3nd pixel, green (second row)
0, 255, 0, 255 # 4nd pixel, green
0, 0, 0, 0 # Empty
0, 0, 0, 0 # Empty
0, 0, 255, 255 # 5rd pixel, blue (third row)
0, 0, 255, 255 # 6rd pixel, blue
0, 0, 0, 0 # Empty
0, 0, 0, 0 # Empty
0, 0, 0, 0 # Empty (last row)
0, 0, 0, 0 # Empty
0, 0, 0, 0 # Empty
0, 0, 0, 0 # Empty
]

I give also the entire method.
Thanks a lot by advance !

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  def self.load_bmp name
    fail unless FileTest.exist?('Media/'+name)
    file = File.open('Media/'+name, 'rb')
    struct = {:name => name}
    file.seek(18, IO::SEEK_CUR)
    # Read width and height
    struct[:width]      = file.read(4).unpack('i')[0]
    struct[:height]     = file.read(4).unpack('i')[0]
    # Calculate closest power of 2
    struct[:real_size]  = 2
    struct[:real_size] *= 2 while struct[:real_size] < struct[:width]
    struct[:real_size] *= 2 while struct[:real_size] < struct[:height]
    file.seek(28, IO::SEEK_CUR)
    # Read data from file
    size = struct[:width]*struct[:height]*3
    data = file.read(size).unpack('C*')
    file.close
    
    corrected = []
    # BGR => RGB & miror y
    for y in 0...(struct[:height])
      for x in 0...(struct[:width])
        z = (struct[:height]-y-1)*struct[:width]*3 + 3*x
        corrected << data[z+2]
        corrected << data[z+1]
        corrected << data[z  ]
        corrected << 255 # Add alpha value
      end
      for x in struct[:width]...struct[:real_size]
        corrected << 0
        corrected << 0
        corrected << 0
        corrected << 0
      end
    end
    for y in struct[:height]...struct[:real_size]
      for x in 0...struct[:real_size]
        corrected << 0
        corrected << 0
        corrected << 0
        corrected << 0
      end
    end
    # Return corrected texture
    struct[:data] = corrected.pack('C*')
    return struct
  end
F9a9ba6663645458aa8630157ed5e71e

Ants

February 1, 2010, February 01, 2010 23:48, permalink

No rating. Login to rate!

OpenGL 2.0 and up supports textures that are not powers of two.

D85d44a0eca045f40e5a31449277c26c

Ben Marini

February 3, 2010, February 03, 2010 16:09, permalink

1 rating. Login to rate!

Here's my functional version, with some test code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
module Bitmap
  def self.bgr_to_rgba(raw_bgr_array, width, height, new_dimension)
    width_padding  = new_dimension - width
    height_padding = new_dimension - height

    # First convert to array of Pixel::BGR objects
    raw_bgr_array.enum_for(:each_slice, 3).map { |a| Pixel::BGR.new(*a) }.
      reverse. # Reverse it
      map { |bgr| bgr.to_rgba }. # Map to array of Pixel::RGBA objects
      enum_for(:each_slice, width).inject([]) { |memo, row| memo << row }. # Create a 2d array based on width
      map { |row|
        row.concat( [Pixel::RGBA.blank] * width_padding )
      }.concat( [ [Pixel::RGBA.blank] * new_dimension ] * height_padding ). # Change dimensions of 2d array, adding blanks
      flatten.map { |rgba| rgba.to_a }.flatten # Flatten everything back into an array of integers
  end

  module Pixel
    class RGBA < Struct.new(:red, :green, :blue, :alpha)
      def self.blank
        new(0,0,0,0)
      end

      def to_a
        [red, green, blue, alpha]
      end
    end

    class BGR < Struct.new(:blue, :green, :red)
      def to_rgba
        RGBA.new(red, green, blue, 255)
      end
    end
  end
end

if __FILE__ == $0
  require "test/unit"
  class TestBgrToRgba < Test::Unit::TestCase
    def test_bgr_to_rgba
      width     = 2
      height    = 3
      real_size = 4 # closest power of 2

      data = [
        255, 0, 0, # 1st pixel, blue (third row)
        255, 0, 0, # 2st pixel, blue
        
        0, 255, 0, # 3nd pixel, green (second row)
        0, 255, 0, # 4nd pixel, green
        
        0, 0, 255, # 5rd pixel, red (first row)
        0, 0, 255, # 6rd pixel, red
      ]

      corrected = [
        255, 0, 0, 255, # 1st pixel, red (first row)
        255, 0, 0, 255, # 2st pixel, red
        0, 0, 0, 0,     # Empty
        0, 0, 0, 0,     # Empty
        
        0, 255, 0, 255, # 3nd pixel, green (second row)
        0, 255, 0, 255, # 4nd pixel, green
        0, 0, 0, 0,     # Empty
        0, 0, 0, 0,     # Empty
        
        0, 0, 255, 255, # 5rd pixel, blue (third row)
        0, 0, 255, 255, # 6rd pixel, blue
        0, 0, 0, 0,     # Empty
        0, 0, 0, 0,     # Empty
        
        0, 0, 0, 0,     # Empty (last row)
        0, 0, 0, 0,     # Empty
        0, 0, 0, 0,     # Empty
        0, 0, 0, 0      # Empty
      ]

      result = Bitmap.bgr_to_rgba(data, width, height, real_size)
      assert_equal corrected, result
    end
  end
end
D85d44a0eca045f40e5a31449277c26c

Ben Marini

February 3, 2010, February 03, 2010 16:09, permalink

No rating. Login to rate!

Here's my functional version: http://gist.github.com/293713

61f1b34f172ee1fbc1af42ab3ce0eca9

darkleo.myopenid.com

February 7, 2010, February 07, 2010 21:44, permalink

No rating. Login to rate!

@Ants : It is obviously much faster with powers of two, at least with ruby-opengl
@Ben Marini : Thank you very much, It will help me a lot, particularly the methods 'map' and 'flatten'

96bfee99a277135b44af65de46150f3f

Alex Baranosky

February 11, 2010, February 11, 2010 02:04, permalink

No rating. Login to rate!

Mine is using Ruby 1.9... I refactored all the code even the stuff loading the bmp from the file. I was not able to test any of that code however so hopefully it is bug free, the spec I show below covers generating the corrected bmp.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class BmpCorrector
  def load_bmp(name)
    bmp, original_bmp_data_array = create_bmp_from_file(name)
    bmp.generate_corrected(original_bmp_data_array)
  end

  def create_bmp_from_file(name)
    file_name = "Media/#{name}"
    fail unless FileTest.exist?(file_name)
    bmp = nil

    original_bmp_data_array = open(file_name, 'rb') do |file|
      width, height = read_width_and_height(file)
      bmp = Bmp.new(height, width)
      read_data(height, width, file)
    end

    return bmp, original_bmp_data_array
  end

  def read_width_and_height(file)
    file.seek(18, IO::SEEK_CUR)
    [file.read(4).unpack('i')[0], file.read(4).unpack('i')[0]]
  end

  def read_data(height, width, file)
    file.seek(28, IO::SEEK_CUR)
    size = width * height * Bmp::BGR_PIXEL_SLICE_WIDTH
    file.read(size).unpack('C*')
  end

end

class Bmp
  BGR_PIXEL_SLICE_WIDTH = 3

  def initialize(height, width)
    @original_height, @original_width = height, width
    @new_dimension = closest_power_of_two
  end

  def generate_corrected(raw_bgr_array)
    right_padding  = @new_dimension - @original_width
    bottom_padding = @new_dimension - @original_height

    pixels = pixels_from_raw_bgr_data(raw_bgr_array)
    pixel_rows_w_out_padding = pixel_2d_array(pixels)
    add_padding(pixel_rows_w_out_padding, bottom_padding, right_padding)
  end

  def pixels_from_raw_bgr_data(raw_bgr_array)
    raw_bgr_array.each_slice(BGR_PIXEL_SLICE_WIDTH).
            collect { |slice| Pixel.from_bgr(*slice) }.
            reverse
  end

  def pixel_2d_array(pixels)
    pixels.each_slice(@original_width).inject([]) { |result, row| result << row }
  end

  def add_padding(pixel_rows, bottom_padding, right_padding)
    corrected_array = pixel_rows.collect {|row| row + Pixel.blanks(right_padding) }
    corrected_array += Pixel.blanks(@new_dimension * bottom_padding)
    corrected_array.flatten.collect(&:to_a).flatten
  end

  def closest_power_of_two
    result  = 2
    result *= 2 while (result < @original_height || result < @original_width)
    result
  end
end

class Pixel

  def initialize(red = 0, green = 0, blue = 0, alpha = 0)
    @red, @green, @blue, @alpha = red, green, blue, alpha
  end

  def self.from_bgr(blue, green, red)
    Pixel.new(red, green, blue, 255)
  end

  def self.blanks(amount = 1)
    Array.new(amount) { Pixel.new }
  end

  def to_a
    [@red, @green, @blue, @alpha]
  end

end

BmpCorrector.new.load_bmp("filename.....")

Spec File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
require "spec"
require 'corrector'

describe Bmp do
  width = 2
  height = 3

  data = [
          255, 0, 0, # 1st pixel, blue (third row)
          255, 0, 0, # 2st pixel, blue

          0, 255, 0, # 3nd pixel, green (second row)
          0, 255, 0, # 4nd pixel, green

          0, 0, 255, # 5rd pixel, red (first row)
          0, 0, 255, # 6rd pixel, red
  ]

  expected_corrected = [
          255, 0, 0, 255, # 1st pixel, red (first row)
          255, 0, 0, 255, # 2st pixel, red
          0, 0, 0, 0, # Empty
          0, 0, 0, 0, # Empty

          0, 255, 0, 255, # 3nd pixel, green (second row)
          0, 255, 0, 255, # 4nd pixel, green
          0, 0, 0, 0, # Empty
          0, 0, 0, 0, # Empty

          0, 0, 255, 255, # 5rd pixel, blue (third row)
          0, 0, 255, 255, # 6rd pixel, blue
          0, 0, 0, 0, # Empty
          0, 0, 0, 0, # Empty

          0, 0, 0, 0, # Empty (last row)
          0, 0, 0, 0, # Empty
          0, 0, 0, 0, # Empty
          0, 0, 0, 0  # Empty
  ]

  it "should correct the bmp properly" do
    actual_corrected = Bmp.new(height, width).generate_corrected(data)
    actual_corrected.should == expected_corrected
  end
end

Your refactoring





Format Copy from initial code

or Cancel