Change access to raw pixel data of FXImage and derivatives
Allow more consistent read/write access to image data, that is also
usable with derived classes like FXJPGImage.
Moreover much faster String based access to the pixel data is possible,
now.
FXImage#data and FXMemoryBuffer is now deprecated.
Lars Kanis
12 years ago
446 | 446 | rb_ary_push(result,to_ruby(colors[i])); |
447 | 447 | return result; |
448 | 448 | } |
449 | ||
450 | FXuint FXRbNumberOfFXColors(VALUE string_or_ary){ | |
451 | FXuint len; | |
452 | ||
453 | if(TYPE(string_or_ary) == T_ARRAY){ | |
454 | len = RARRAY_LEN(string_or_ary); | |
455 | }else{ | |
456 | Check_Type(string_or_ary,T_STRING); | |
457 | if(RSTRING_LEN(string_or_ary) % sizeof(FXColor) != 0 ) | |
458 | rb_raise( rb_eArgError, "String size is no multiple of %lu", sizeof(FXColor) ); | |
459 | len = RSTRING_LEN(string_or_ary) / sizeof(FXColor); | |
460 | } | |
461 | return len; | |
462 | } | |
463 | ||
464 | FXColor *FXRbConvertToFXColors(VALUE string_or_ary){ | |
465 | FXColor* pix=0; | |
466 | if(TYPE(string_or_ary) == T_ARRAY){ | |
467 | if(FXMALLOC(&pix,FXColor,RARRAY_LEN(string_or_ary))){ | |
468 | for(long i=0; i<RARRAY_LEN(string_or_ary); i++){ | |
469 | pix[i]=static_cast<FXColor>(NUM2UINT(rb_ary_entry(string_or_ary,i))); | |
470 | } | |
471 | } | |
472 | }else{ | |
473 | if(FXMALLOC(&pix,FXColor,RSTRING_LEN(string_or_ary)/sizeof(FXColor))){ | |
474 | memcpy(pix, RSTRING_PTR(string_or_ary), RSTRING_LEN(string_or_ary)); | |
475 | } | |
476 | } | |
477 | return pix; | |
478 | } | |
449 | 479 | |
450 | 480 | //---------------------------------------------------------------------- |
451 | 481 |
149 | 149 | |
150 | 150 | // Returns a Ruby array of FXColor values |
151 | 151 | extern VALUE FXRbMakeColorArray(const FXColor* colors,FXint w,FXint h); |
152 | ||
153 | // Returns a number of FXColor elements in the argument | |
154 | extern FXuint FXRbNumberOfFXColors(VALUE string_or_ary); | |
155 | ||
156 | // Allocate a FXColor buffer and populate with data | |
157 | extern FXColor *FXRbConvertToFXColors(VALUE string_or_ary); | |
152 | 158 | |
153 | 159 | extern void* FXRbGetExpectedData(VALUE recv,FXSelector key,VALUE data); |
154 | 160 |
2409 | 2409 | class FXImage |
2410 | 2410 | def data(*args) # :nodoc: |
2411 | 2411 | getData(*args) |
2412 | end | |
2413 | def pixels=(*args) # :nodoc: | |
2414 | setPixels(*args) | |
2412 | 2415 | end |
2413 | 2416 | def options(*args) # :nodoc: |
2414 | 2417 | getOptions(*args) |
38 | 38 | |
39 | 39 | class FXImage < FXDrawable |
40 | 40 | |
41 | # Pixel data [FXMemoryBuffer] | |
41 | # [deprecated] Pixel data [FXMemoryBuffer] | |
42 | 42 | attr_reader :data |
43 | ||
44 | # Array of colors of all image pixels. Can also be written as String of raw [RGBA] values. | |
45 | attr_accessor :pixels | |
46 | ||
43 | 47 | |
44 | 48 | # Option flags [Integer] |
45 | 49 | attr_accessor :options |
46 | 50 | |
47 | 51 | # |
48 | 52 | # Create an image. If a client-side pixel buffer has been specified, |
49 | # the image does not own the pixel buffer unless the +IMAGE_OWNED+ flag is | |
50 | # set. If the +IMAGE_OWNED+ flag is set but a +nil+ pixel buffer is | |
53 | # the image owns the pixel buffer. | |
54 | # If the +IMAGE_OWNED+ flag is set but a +nil+ pixel buffer is | |
51 | 55 | # passed, a pixel buffer will be automatically created and will be owned |
52 | 56 | # by the image. The flags +IMAGE_SHMI+ and +IMAGE_SHMP+ may be specified for |
53 | 57 | # large images to instruct #render to use shared memory to communicate |
56 | 60 | # ==== Parameters: |
57 | 61 | # |
58 | 62 | # +a+:: an application instance [FXApp] |
59 | # +pixels+:: pixels [Array of FXColor values] | |
63 | # +pixels+:: pixels [Array of FXColor values or string of raw [RGBA] values] | |
60 | 64 | # +opts+:: image options [Integer] |
61 | 65 | # +width+:: image width [Integer] |
62 | 66 | # +height+:: image height [Integer] |
74 | 78 | # |
75 | 79 | # ==== Parameters: |
76 | 80 | # |
77 | # +pix+:: the array of FXColor values. | |
81 | # +pixels+:: pixels [Array of FXColor values or string of raw [RGBA] values] | |
78 | 82 | # +opts+:: image options [Integer] |
79 | 83 | # +width+:: image width [Integer] |
80 | 84 | # +height+:: image height [Integer] |
81 | 85 | # |
82 | def setData(pix, opts=0, width=nil, height=nil) | |
86 | def setPixels(pixels, opts=0, width=nil, height=nil) | |
83 | 87 | end |
88 | ||
89 | # | |
90 | # Return a String of the raw color representation of all image pixels. | |
91 | # | |
92 | # Image pixels are stored bytewise as [RGBA] values. | |
93 | # | |
94 | def pixel_string ; end | |
84 | 95 | |
85 | 96 | # |
86 | 97 | # Return the color of the pixel at (_x_, _y_). |
0 | 0 | module Fox |
1 | # This class is deprecated. Use FXImage methods instead. | |
1 | 2 | class FXMemoryBuffer |
2 | 3 | |
3 | 4 | # |
54 | 54 | * large images to instruct render() to use shared memory to communicate |
55 | 55 | * with the server. |
56 | 56 | */ |
57 | FXImage(FXApp* a,VALUE ary=Qnil,FXuint opts=0,FXint w=1,FXint h=1){ | |
57 | FXImage(FXApp* a,VALUE string_or_ary=Qnil,FXuint opts=0,FXint w=1,FXint h=1){ | |
58 | 58 | FXColor* pix=0; |
59 | if(!NIL_P(ary)){ | |
60 | Check_Type(ary,T_ARRAY); | |
61 | if(FXMALLOC(&pix,FXColor,RARRAY_LEN(ary))){ | |
62 | for(long i=0; i<RARRAY_LEN(ary); i++){ | |
63 | pix[i]=static_cast<FXColor>(NUM2UINT(rb_ary_entry(ary,i))); | |
64 | } | |
65 | } | |
66 | opts&=IMAGE_OWNED; | |
59 | if(!NIL_P(string_or_ary)){ | |
60 | FXuint len=FXRbNumberOfFXColors(string_or_ary); | |
61 | if(w*h != len){ | |
62 | rb_raise( rb_eArgError, "Array size does not match image size" ); | |
67 | 63 | } |
64 | pix=FXRbConvertToFXColors(string_or_ary); | |
65 | opts|=IMAGE_OWNED; | |
66 | } | |
68 | 67 | return new FXRbImage(a,pix,opts,w,h); |
69 | } | |
68 | } | |
70 | 69 | |
71 | 70 | /// To get to the pixel data |
72 | 71 | FXMemoryBuffer *getData() const { |
73 | 72 | if(self->getData()){ |
74 | 73 | return new FXMemoryBuffer(self->getData(),self->getWidth()*self->getHeight()); |
75 | } | |
74 | } | |
76 | 75 | else{ |
77 | 76 | return 0; |
78 | } | |
79 | } | |
77 | } | |
78 | } | |
80 | 79 | } |
81 | 80 | |
82 | 81 | /// To get to the option flags |
92 | 91 | * The server-side representation of the image, if it exists, is not updated. |
93 | 92 | * This can be done by calling render(). |
94 | 93 | */ |
95 | void setData(VALUE ary,FXuint opts=0,VALUE w=Qnil,VALUE h=Qnil){ | |
96 | FXColor* pix=0; | |
97 | Check_Type(ary,T_ARRAY); | |
98 | if( ( (NIL_P(w) || NIL_P(h)) && self->getWidth()*self->getHeight() != RARRAY_LEN(ary)) || | |
99 | (!(NIL_P(w) || NIL_P(h)) && NUM2UINT(w)*NUM2UINT(h) != RARRAY_LEN(ary))){ | |
100 | rb_raise( rb_eArgError, "array size does not match image size" ); | |
101 | } | |
102 | if(FXMALLOC(&pix,FXColor,RARRAY_LEN(ary))){ | |
103 | for(long i=0; i<RARRAY_LEN(ary); i++){ | |
104 | pix[i]=static_cast<FXColor>(NUM2UINT(rb_ary_entry(ary,i))); | |
105 | } | |
106 | } | |
94 | void setPixels(VALUE string_or_ary,FXuint opts=0,VALUE w=Qnil,VALUE h=Qnil){ | |
95 | FXuint len=FXRbNumberOfFXColors(string_or_ary); | |
96 | if( ( (NIL_P(w) || NIL_P(h)) && self->getWidth()*self->getHeight() != len) || | |
97 | (!(NIL_P(w) || NIL_P(h)) && NUM2UINT(w)*NUM2UINT(h) != len)){ | |
98 | rb_raise( rb_eArgError, "Array size does not match image size" ); | |
99 | } | |
100 | ||
101 | FXColor* pix=FXRbConvertToFXColors(string_or_ary); | |
107 | 102 | opts|=IMAGE_OWNED; |
108 | 103 | if( NIL_P(w) || NIL_P(h) ){ |
109 | 104 | self->setData(pix,opts); |
110 | 105 | }else{ |
111 | 106 | self->setData(pix,opts,NUM2UINT(w),NUM2UINT(h)); |
107 | } | |
108 | } | |
109 | ||
110 | VALUE pixels(){ | |
111 | FXColor* data = self->getData(); | |
112 | if (data) { | |
113 | FXuint size = self->getWidth()*self->getHeight(); | |
114 | VALUE ary = rb_ary_new2(size); | |
115 | for (int i = 0; i < size; i++) | |
116 | rb_ary_store(ary, i, UINT2NUM(data[i])); | |
117 | return ary; | |
118 | } else { | |
119 | return Qnil; | |
120 | } | |
121 | } | |
122 | ||
123 | VALUE pixel_string(){ | |
124 | FXColor* data = self->getData(); | |
125 | if (data) { | |
126 | return rb_str_new((char*)data, self->getWidth()*self->getHeight()*sizeof(FXColor)); | |
127 | } else { | |
128 | return Qnil; | |
112 | 129 | } |
113 | 130 | } |
114 | 131 | } |
10 | 10 | |
11 | 11 | def test_default_constructor_args_1 |
12 | 12 | img = FXImage.new(app) |
13 | assert_same(nil, img.data) | |
13 | assert_same(nil, img.pixels) | |
14 | 14 | assert_equal(0, img.options) |
15 | 15 | assert_equal(1, img.width) |
16 | 16 | assert_equal(1, img.height) |
18 | 18 | |
19 | 19 | def test_default_constructor_args_2 |
20 | 20 | img = FXImage.new(app, nil) |
21 | assert_same(nil, img.data) | |
21 | assert_same(nil, img.pixels) | |
22 | 22 | assert_equal(0, img.options) |
23 | 23 | assert_equal(1, img.width) |
24 | 24 | assert_equal(1, img.height) |
26 | 26 | |
27 | 27 | def test_default_constructor_args_3 |
28 | 28 | img = FXImage.new(app, nil, 0) |
29 | assert_same(nil, img.data) | |
29 | assert_same(nil, img.pixels) | |
30 | 30 | assert_equal(0, img.options) |
31 | 31 | assert_equal(1, img.width) |
32 | 32 | assert_equal(1, img.height) |
34 | 34 | |
35 | 35 | def test_default_constructor_args_4 |
36 | 36 | img = FXImage.new(app, nil, 0, 1) |
37 | assert_same(nil, img.data) | |
37 | assert_same(nil, img.pixels) | |
38 | 38 | assert_equal(0, img.options) |
39 | 39 | assert_equal(1, img.width) |
40 | 40 | assert_equal(1, img.height) |
42 | 42 | |
43 | 43 | def test_default_constructor_args_5 |
44 | 44 | img = FXImage.new(app, nil, 0, 1, 1) |
45 | assert_same(nil, img.data) | |
45 | assert_same(nil, img.pixels) | |
46 | 46 | assert_equal(0, img.options) |
47 | 47 | assert_equal(1, img.width) |
48 | 48 | assert_equal(1, img.height) |
58 | 58 | assert_equal(IMAGE_OWNED, img.options) |
59 | 59 | end |
60 | 60 | |
61 | def test_setData | |
61 | def test_setPixels | |
62 | 62 | img = FXImage.new(app, nil, 0, 2, 2) |
63 | img.setData([0x12345678, 2, 3, 4]) | |
63 | img.pixels = [0x12345678, 2, 3, 4] | |
64 | 64 | assert_equal(IMAGE_OWNED, img.options) |
65 | assert_equal(2*2, img.data.size) | |
66 | assert_equal([0x12345678, 2, 3, 4], img.data.to_a) | |
65 | assert_equal([0x12345678, 2, 3, 4], img.pixels) | |
67 | 66 | end |
68 | 67 | |
69 | def test_setData2 | |
68 | def test_setPixels2 | |
70 | 69 | img = FXImage.new(app) |
71 | img.setData([0x12345678, 2], 0, 2, 1) | |
70 | img.setPixels([0x12345678, 2], 0, 2, 1) | |
72 | 71 | assert_equal(IMAGE_OWNED, img.options) |
73 | assert_equal(2*1, img.data.size) | |
74 | assert_equal([0x12345678, 2], img.data.to_a) | |
72 | assert_equal([0x12345678, 2], img.pixels) | |
73 | end | |
74 | ||
75 | def test_setPixels_string | |
76 | img = FXImage.new(app, nil, 0, 2, 1) | |
77 | img.pixels = "rgbaRGBA" | |
78 | assert_equal(IMAGE_OWNED, img.options) | |
79 | assert_equal("rgbaRGBA", img.pixel_string) | |
75 | 80 | end |
76 | 81 | |
77 | 82 | def test_create |
80 | 85 | # the data should go away after we call create. |
81 | 86 | # |
82 | 87 | img = FXImage.new(app, nil, IMAGE_OWNED) |
83 | assert_not_nil(img.data) | |
88 | assert_not_nil(img.pixels) | |
84 | 89 | img.create |
85 | assert_nil(img.data) | |
90 | assert_nil(img.pixels) | |
86 | 91 | |
87 | 92 | # |
88 | 93 | # If the image owns its pixel data and IMAGE_KEEP was specified, |
89 | 94 | # the data should stay after we call create. |
90 | 95 | # |
91 | 96 | img = FXImage.new(app, nil, IMAGE_KEEP|IMAGE_OWNED) |
92 | assert_not_nil(img.data) | |
97 | assert_not_nil(img.pixels) | |
93 | 98 | img.create |
94 | assert_not_nil(img.data) | |
99 | assert_not_nil(img.pixels) | |
100 | end | |
101 | ||
102 | def test_create_with_data | |
103 | img = FXImage.new(app, "rgbaRGBA", 0, 1, 2) | |
104 | assert_equal("rgbaRGBA", img.pixel_string) | |
105 | img.create | |
106 | assert_nil(img.pixels) | |
107 | ||
108 | img = FXImage.new(app, [0x12345678], IMAGE_KEEP|IMAGE_OWNED) | |
109 | assert_equal([0x12345678], img.pixels) | |
110 | img.create | |
111 | assert_not_nil(img.pixels) | |
95 | 112 | end |
96 | 113 | |
97 | 114 | # |
105 | 122 | # |
106 | 123 | img = FXImage.new(app) |
107 | 124 | img.create |
108 | assert_nil(img.data) | |
125 | assert_nil(img.pixels) | |
109 | 126 | assert_equal(0, img.options&IMAGE_OWNED) |
110 | 127 | img.restore |
111 | assert_not_nil(img.data) | |
128 | assert_not_nil(img.pixels) | |
112 | 129 | assert_not_equal(0, img.options&IMAGE_OWNED) |
113 | 130 | end |
114 | 131 |