In the release of 0.2.2, Nana C++ Library provides a mechanism which can flexibly configure the image processing algorithms. For an abstract algorithm, there would be many implementations. For example, Nana C++ Library provides two stretch algorithms, they are bilinear interoplation and proximal interoplation, you can switch the algorithms under your requirement.
See the example at:
http://stdex.sourceforge.net/help/paint/image_process_selector.htm
The algorithms provided by Nana are implemented without specific platform instructions for cross-platform, they should work fine in different compiler environment. In some situations, an extreme efficiency is desired. Although the Nana C++ Library does not provide such an algorithm with specific instructions, it is possible to implement the algorithm by yourself for your requirment, and configure it to make Nana apply it.
Let’s implement a blend algorithm with MMX instruction.
To implement a blend algorithm that can be applied by Nana C++ Library, we have to implement the algorithm with class blend_interface, the class is defined by Nana in header file <nana/paint/image_process_interface.hpp>.
class blend_interface
{
public:
virtual ~blend_interface() = 0;
virtual void process(nana::paint::drawable_type dw_dst, const nana::rectangle& r_dst, const paint::pixel_buffer& s_pixbuf, const nana::point& s_pos, double fade_rate) const = 0;
};
dw_dst: the destination object.
r_dst: the destination blend area.
s_pixbuf: the source pixel buffer.
s_pos: the source blend coordinate.
fade_rate: the rate at blend.
The blend area is always valid, it is calculated by Nana before passing it to the function. So we can use it without check.
The semantics of this interface is:
dw_dst = dw_dst * fade_rate + s_pixbuf * (1 – fade_rate)
Let’s start!
#include <nana/paint/image_process_interface.hpp>
class blend_mmx
: public nana::paint::image_process::blend_interface
{
virtual void process(nana::paint::drawable_type dw_dst, const nana::rectangle& r_dst, const nana::paint::pixel_buffer& s_pixbuf, const nana::point& s_pos, double fade_rate) const
{
nana::paint::pixel_buffer d_pixbuf(dw_dst, r_dst.y, r_dst.height);
nana::pixel_rgb_t * d_rgb = d_pixbuf.raw_ptr();
if(d_rgb)
{
d_rgb += r_dst.x;
nana::pixel_rgb_t * s_rgb = s_pixbuf.raw_ptr(s_pos.y) + s_pos.x;
const unsigned rest = r_dst.width & 0x3;
const unsigned length_align4 = r_dst.width – rest;
unsigned d_step_width = d_pixbuf.size().width;
unsigned s_step_width = s_pixbuf.size().width;
unsigned i_d_rate = static_cast<unsigned>(fade_rate * 255);
unsigned i_s_rate = 255 – i_d_rate;
__asm
{
pxor xmm7, xmm7
movd xmm0, i_d_rate
punpcklwd xmm0, xmm0
punpcklwd xmm0, xmm0
punpcklwd xmm0, xmm0
movd xmm1, i_s_rate
punpcklwd xmm1, xmm1
punpcklwd xmm1, xmm1
punpcklwd xmm1, xmm1
}
for(unsigned line = 0; line < r_dst.height; ++line)
{
__asm
{
push eax
push ebx
push edx
mov edx, dword ptr[length_align4]
mov eax, dword ptr[d_rgb]
mov ebx, dword ptr[s_rgb]
lea edx, [edx * 4 + eax]
blend_mmx_3_for_each_pixel_in_line_loop_start:
cmp eax, edx
je blend_mmx_3_for_each_pixel_in_line_loop_end
//eax[0], eax[1]
movq xmm2, qword ptr[eax]
movq xmm3, qword ptr[ebx]
punpcklbw xmm2, xmm7
punpcklbw xmm3, xmm7
pmullw xmm2, xmm0
pmullw xmm3, xmm1
paddw xmm2, xmm3
psrlw xmm2, 8
packuswb xmm2, xmm7
movq qword ptr[eax], xmm2
//eax[2], eax[3]
movq xmm2, qword ptr[eax + 8]
movq xmm3, qword ptr[ebx + 8]
punpcklbw xmm2, xmm7
punpcklbw xmm3, xmm7
pmullw xmm2, xmm0
pmullw xmm3, xmm1
paddw xmm2, xmm3
psrlw xmm2, 8
packuswb xmm2, xmm7
movq qword ptr[eax + 8], xmm2
add eax, 16
add ebx, 16
jmp blend_mmx_3_for_each_pixel_in_line_loop_start
blend_mmx_3_for_each_pixel_in_line_loop_end:
push ecx
mov ecx, dword ptr[rest]
cmp ecx, 0
je blend_mmx_3_for_each_rest_pixel_loop_end
blend_mmx_3_for_each_rest_pixel_loop_start:
movd xmm2, dword ptr[eax]
movd xmm3, dword ptr[ebx]
punpcklbw xmm2, xmm7
punpcklbw xmm3, xmm7
pmullw xmm2, xmm0
pmullw xmm3, xmm1
paddw xmm2, xmm3
psrlw xmm2, 8
packuswb xmm2, xmm7
movd dword ptr[eax], xmm2
add eax, 4
add ebx, 4
loop blend_mmx_3_for_each_rest_pixel_loop_start
blend_mmx_3_for_each_rest_pixel_loop_end:
pop ecx
pop edx
pop ebx
pop eax
}
d_rgb += d_step_width;
s_rgb += s_step_width;
}
__asm emms
nana::rectangle r = r_dst;
r.y = 0;
d_pixbuf.paste(r, dw_dst, r_dst.x, r_dst.y);
}
}
};
Apply it!
#include <nana/gui/wvl.hpp>
#include <nana/gui/timer.hpp>
#include <nana/paint/image_process_selector.hpp>
//Include the definition of blend_mmx that we implemented before.
class blend_form
: public nana::gui::form
{
public:
blend_form()
:form(nana::gui::API::make_center(450, 300))
,fade_rate_(0.1), delta_(0.1)
{
this->show();
nana::paint::image img(STR(“image01.bmp”));
s_graph_.make(img.size().width, img.size().height);
img.paste(s_graph_, 0, 0);
d_image_.open(STR(“image02.bmp”));
d_graph_.make(d_image_.size().width, d_image_.size().height);
timer_.make_tick(nana::make_fun(*this, &tsform::_m_blend));
timer_.interval(10);
//Install the blend_mmx and apply it.
nana::paint::image_process::selector sl;
sl.add_blend<blend_mmx>(“blend_mmx”); //Give it a name
sl.blend(“blend_mmx”); //Apply it
}
private:
void _m_blend()
{
fade_rate_ += delta_;
if(fade_rate_ > 1)
{
fade_rate_ = 1;
delta_ = -0.01;
}
else if(fade_rate_ < 0)
{
fade_rate_ = 0;
delta_ = 0.01;
}
d_image_.paste(d_graph_, 0, 0);
//The blend operation, it employes the blend_mmx algorithm.
s_graph_.blend(d_graph_, 0, 0, fade_rate_);
nana::gui::drawing drawing(*this);
drawing.clear();
drawing.bitblt(0, 0, 450, 300, d_graph_, 0, 0);
drawing.update();
}
private:
double fade_rate_;
double delta_;
timer timer_;
nana::paint::graphics s_graph_, d_graph_;
nana::paint::image d_image_;
};
It shows a method that user can implement an algorithm instead of the algorithm that Nana provides for various requirement.
Enjoy!