Skip to Content

h4. Introduction

A couple of days ago in the Web Dynpro ABAP forum, the question was posed, can we use WDA to create a thumbnail image of an uploaded image for use in indexes etc. I didn’t think ABAP had any image processing capability, so I was very happily surprised when Thomas Jung  (http://www.sdn.sap.com/irj/scn/bc?u=5%2bhlk9c3p0q%3d) pointed out that he had written some code to use the functionality of the AGS to do some simple image manipulation.  (ABAP Bitmap Image Processing Class)

Thomas hadn’t built in image resizing capabilities into his solution, so I decided to give it a go.

Image Resizing/Resampling 

Before I show the resulting code, it is probably worth going over the concept of resampling/resizing images. There are many different ways in which you can skin this particular cat. Especially when you start thinking about enlarging images, the logic used can quickly become quite complex. 

There are many different algorithms that you can use (a comparison of result linked here)  (http://www.lassekolb.info/gim35_downsampling.htm), again like Thomas, I found Wikipedia  (http://en.wikipedia.org/wiki/Category:Multivariate_interpolation) to be a good reference site. Because I just wanted to see if it were possible, I went with the simplest solution which is called Nearest-neighbor interpolation (also known as proximal interpolation or point sampling) (thank you to Wikipedia for further extending my vocabulary).

The nearest neighbour approach basically squashes, or expands, an image by throwing away data (when squashing) or adding more of the same data (when expanding). It results in really blocky images when you use it to scale up – but reasonable images when used to scale down. I found a

very informative link to an article which explained how it could be implemented (in Java)

. I used this as the basis for my implementation.

Code time

OK, enough theory – here’s the results! 

I extended Thomas’ class (ZCL_ABAP_BITMAP) to have one more method, called, unsurprisingly “TRANSFORM_RESIZE”. It takes two required input parameters:

!https://weblogs.sdn.sap.com/weblogs/images/251723740/method_signature.jpg|height=96|alt=image|width=568|src=https://weblogs.sdn.sap.com/weblogs/images/251723740/method_signature.jpg|border=0! And returns Thomas’s exception class if anything goes wrong.

And here’s the code – it isn’t pretty, I really should add some comments it – and you can blame the huge amount of hard coded constants on Thomas 😉 – I’m just copying his bitmap handling routines! But it works, and hopeful the theory links about can explain what is going on. = l_datal_counter(l_width).<br />  enddo.<br /><br /><br /><br />  l_mod = ( new_width * l_bytes_per_pixel ) mod 4.<br />  l_new_pad = 4 – l_mod.<br />  if l_new_pad = 4.<br />    l_new_pad = 0.<br />  endif.<br /><br /><br /><br />  loop at lt_rows assigning <wa_row>.<br />    clear l_new_row.<br />    do new_width times.<br />      l_counter2 =  l_bytes_per_pixel * floor( ( sy-index – 1 ) * l_x_factor ).<br />      concatenate l_new_row <wa_row>l_counter2(l_bytes_per_pixel)  into l_new_row in byte mode.

    enddo.

    if l_new_pad > 0.

      do l_new_pad times.

        concatenate  l_new_row l_null into l_new_row in byte mode.

      enddo.

    endif.

    concatenate  l_new_data l_new_row into l_new_data in byte mode.

  endloop.

  data x_new_image type xstring.

  concatenate gx_content(data_offset) l_new_data into x_new_image in byte mode.

  data x_header type xstring.

  x_header = gx_content0(data_offset).<br /><br />  data x_size(4) type x.<br />  data i_size type i.<br />  i_size = xstrlen( x_new_image ).<br />  write4 i_size x_size.<br />  concatenate x_header0(2) x_size x_header6 into x_header in byte mode.<br /><br />  data x_data_size(4) type x.<br />  data i_data_size type i.<br />  i_data_size = xstrlen( l_new_data ).<br />  write4 i_data_size x_data_size.<br />  concatenate x_header0(34) x_data_size x_header38 into x_header in byte mode.<br /><br />  data x_width(4) type x.<br />  data x_height(4) type x.<br />  data i_width type i.<br />  data i_height type i.<br />  i_width = new_width.<br />  i_height = new_height.<br />  write4 i_width x_width.<br />  write4 i_height x_height.<br /><br />  concatenate x_header0(18) x_width x_height x_header26 into x_header in byte mode.<br />  data x_hres(4) type x.<br />  data x_vres(4) type x.<br /><br />  x_hres = x_header38(4).

  x_vres = x_header42(4).<br />  concatenate x_header0(38) x_vres x_hres x_header+46 into x_header in byte mode.

  concatenate x_header  l_new_data into x_new_image in byte mode.

  gx_content = x_new_image.

  me->read_bitmap_header( ).

endmethod.

And the results

Slightly updating Thomas’s demo WDA, I added an additional option that resized the current image to 100px height and whatever appropriate width.

So first after loading the lovely  double rainbow ( I can’t get the song out of my head!)  (http://www.youtube.com/watch?v=MX0D4oZwCsA)

image

And then actually clicking that resize menu option, we have our image resized! 

!https://weblogs.sdn.sap.com/weblogs/images/251723740/resized_rainbow.jpg|height=273|alt=image|width=679|src=https://weblogs.sdn.sap.com/weblogs/images/251723740/resized_rainbow.jpg|border=0!</body>

To report this post you need to login first.

10 Comments

You must be Logged on to comment or reply to a post.

  1. Alvaro Tejada Galindo
    Chris:

    I thought Thomas was crazy enough to deal with bits when he mailed me telling that he found a way to manage images in SAP…but looking at your blog…I gotta say…Double Woaw!
    You guys make my life happier -:D

    Greetings,
    Blag.

    (0) 
    1. Chris Paine
      Hi Blag,
      I’m happy that you like it. It is a bit mad what you can do when you have people like Thomas to give you advice and hints!
      Very happy I could make anyone happier with a little code!
      Cheers,
      Chris
      (0) 
  2. Carsten Kasper
    …but why would I not use the CL_IGS_IMAGE_CONVERTER for this? It is fast, easy to use and handles GIF, TIFF, PNG, JPG and of course BMP.

    cheers Carsten

    (0) 
    1. Chris Paine
      Hi Carsten,

      Mainly because I didn’t know it existed, as I have to keep telling my daughter, I’m not omniscient. (she’s in the really into asking “why?” at the moment, which I, to my detriment, am encouraging.)

      Having looked, I’m not sure that the class actually allows any manipulation of the size of the image. If you could provide some example of the use in this manner I’d be quite interested.

      (0) 
      1. Carsten Kasper
        Hi Chris,

        After having a look in the system, I saw function module RS_IGS_IMGCON. It may be even more straight forward.
        For the class I mentioned it works like this. You get an instance of the class. You push the image to the class with SET_IMAGE(). Next you set the global attributes on your class instance (like width, height). Then you call GET_IMAGE() to recieve the converted xstring.

        Here is a little sample code for the use of function module RS_IGS_IMGCONV. For the CL_IGS_IMAGE_CONVERTER I do not have the calls mentioned above ready in ABAP, but I am sure you can get them together with the procedure I gave.

        {code}
        DATA:
          gv_width          type      i,
          gv_height         type        i,
          gv_error          TYPE        i,
          gv_error_msg      TYPE        string,
          gv_thumb_size     TYPE        i,
          gv_thumb          TYPE        xstring,
          gv_xsize          TYPE        i,
          gv_xstring        TYPE        xstring,

            gv_xsize = XSTRLEN( gv_xstring ).
        *   Convert using IGS
            CALL FUNCTION ‘RS_IGS_IMGCONV’
              EXPORTING
                iv_source_image  = gv_xstring
                iv_source_type   = ‘image/jpeg’
                iv_source_size   = gv_xsize
                iv_dest_pxwidth  = gv_width
                iv_dest_pxheight = gv_heigth
                iv_dest_type     = ‘image/jpeg’
              IMPORTING
                ev_dest_image    = gv_thumb
                ev_dest_size     = gv_thumb_size
                ev_error_code    = gv_error
                ev_error_message = gv_error_msg.
        {code}

        (0) 
        1. Chris Paine
          Thanks Carsten,

          That does look a lot easier than the bit level manipulation that I was using!

          Just goes to prove as I wrote in the comments request section “It’s amazing the resources that are out there and the knowledge that exists on SCN. Almost anything is possible!” Thanks for sharing that with us, I’m sure it will make life a lot easier for someone!
          For me, I only did this for a bit of fun anyway – and that I succeeded in having! 🙂

          Thanks for taking the time to share your knowledge!

          (0) 
  3. Michelle Crapo
    It’s another trick I can save for later.  I’m just starting to develop in WDA. 

    Thank you Carsten as well.

    I liked the double rainbow song.  Of course, now I have it in my head.

    Michelle

    (0) 
    1. Chris Paine
      Hi Michelle,
      yes, it is darn catchy that song. I first heard it on an Engadget podcast, although it makes more sense when you see the original video that it is based on – the guy who filmed it seems to be a little more than legally happy 😉 It isn’t the worst song to get stuck in your head though – much better than anything by the Wiggles (those with small children living in Australia can hopefully appreciate that.)
      Good luck with your steps into WDA. I’m sure we’ll hear from you. Thanks for the comments.
      Chris
      (0) 
  4. Aaron Morden
    A while back I had to resize some images.  I also created a new instance method for Thomas’s class.  My method imported the new height & width (im_width & im_height).

    METHOD resize_image.
      zcl_abap_bitmap=>check_igs_setup( ).
      DATA: l_igs_imgconv TYPE REF TO cl_igs_image_converter,
            l_img_blob    TYPE w3mimetabtype,
            l_img_size    TYPE w3param-cont_len,
            l_bmp_xstream TYPE xstring.
      CREATE OBJECT l_igs_imgconv.

      l_img_size = XSTRLEN( gx_content ).
      CALL FUNCTION ‘SCMS_XSTRING_TO_BINARY’
        EXPORTING
          buffer     = gx_content
        TABLES
          binary_tab = l_img_blob.
      CALL METHOD l_igs_imgconv->set_image
        EXPORTING
          blob      = l_img_blob
          blob_size = l_img_size.

      l_igs_imgconv->width  = im_width.
      l_igs_imgconv->height = im_height.
      l_igs_imgconv->output = ‘image/x-ms-bmp’.
      l_igs_imgconv->input  = ‘image/x-ms-bmp’.

      CALL METHOD l_igs_imgconv->execute
        EXCEPTIONS
          OTHERS = 1.

      IF sy-subrc IS INITIAL.
        CALL METHOD l_igs_imgconv->get_image
          IMPORTING
            blob      = l_img_blob
            blob_size = l_img_size.

        CALL FUNCTION ‘SCMS_BINARY_TO_XSTRING’
          EXPORTING
            input_length = l_img_size
          IMPORTING
            buffer       = me->gx_content
          TABLES
            binary_tab   = l_img_blob
          EXCEPTIONS
            failed       = 1
            OTHERS       = 2.
        IF sy-subrc NE 0.
          RAISE EXCEPTION TYPE zcx_abap_bitmap.
        ENDIF.
      ENDIF.

      me->read_bitmap_header( ).

    ENDMETHOD.

    (0) 
    1. Chris Paine
      Thanks Aaron!
      Seems like a few people have attacked this problem before! Just goes to show the breadth of knowledge here on SCN.
      Thanks for taking the time to add your solution!
      Cheers,
      Chris
      (0) 

Leave a Reply