Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
thomas_jung
Developer Advocate
Developer Advocate
33,776

Introduction


Last week Alvaro Tejada (Blag) posted a The specified item was not found.. Well I got all caught up in the fun challenge and now here are the results for your downloading pleasure.

What Blag wanted to do was simply flip an existing Bitmap that he was reading out of the BDS (Business Document Store).  The problem that he was facing is that there is no easy method for doing this in ABAP. He tried just reversing the Byte Stream, but quickly found out that this corrupted the image.  A bitmap isn't just stored as a simple data stream - it has header information and a complex structure that must be taken into consideration when doing any manipulations on it.

But many other languages have simple processes for working with Bitmaps - what was wrong with ABAP.  Well these other languages just have libraries that have already been created that abstract the actual process of working with the Bitmap.  These libraries directly expose attributes like the bitmap height and width, so that programmers don't have to know the detailed structure of the Bitmap header.  They also usually have methods to allow for basic manipulation of the Bitmap.

To my best knowledge, no one in ABAP had created such a library.  So while it was perfectly possible to manipulate the Bitmap at a low level in ABAP, it was more tedious than necessarily because it didn't have one of these helper libraries.  Historically ABAP has been all about processing business data, so it hasn't been a big issue that you couldn't flip bitmaps or convert them to grey scale.  But with ABAP being used increasingly for forms output and web pages, this is functionality that might be useful from time to time.  So I decided to take the rough solution that I built for Blag and turn it into a reusable bitmap class.

Bitmap Processing


Before we get into the actual solution of the ABAP Bitmap Library, I thought I would cover some basics of the processing that is going on within the library.  If you are interested in how Bitmaps are stored and processed then keep reading this section. If you really couldn't care less and just want to get started using the library, then skip down to the next section (after all the purpose of creating this library was so people wouldn't have to care how a bitmap works internally).

When I started looking into Blag's problem, I had no real experience with how bitmaps are structured.  So I started my investigation via the internet.  I found lots of good articles on Bitmaps, but probably the best summary of information I found was on Wikipedia.

The linked Wikipedia article does an excellent job of describing the inner layout of the Bitmap - how you have a section for the header information and then a separate section for the data.  It also describes for each pixel is stored.  The color of the pixel (in 24 bits per pixel) is stored as three separate integers - one each for Red, Green, and Blue.  It is important to understand that you can't just manipulate data within the Bitmap at the byte level, but instead the pixel level so you can keep from altering the RGB color as you move bytes around.

The second complexity is that bitmaps are not stored top to bottom within the data area.  Instead the first visible row of data on the screen is stored as the list "line" in the bitmap data area.  All the data is also stored together in a single stream. Therefore for easier processing you generally break the stream down by horizontal row. But because processing is easier if divisible by 4, null-bytes are added to the end of the line to pad the file.  All of these aspects are especially important when trying to rotate a bitmap.

The final complexity that I faced in my processing was that much of the data in the bitmap header was stored as either 2 or 4 byte DWORDs. This meant that I couldn't just cast these byte values into ABAP Integers.  Instead I had to create small macros to change the byte order.

The ABAP Bitmap Library


OK, enough of the deep stuff.  Now it is time to see just how easy we can make working with Bitmaps in ABAP.  For that purpose I have created a Class called ZCL_ABAP_BITMAP.




As you can see there are plenty of methods in this class.  Let's start with the Static methods.  You see the class is marked as Private Instantiation only, therefore the static methods are the only way to create an instance of the class.

Each of the static methods is a path to provide the bitmap source and return an instance of the class.  We have the basic create from bitmap methods like CREATE_FROM_BYTESTEAM (type XSTRING) and CREATE_FROM_BYTETABLE (type standard table of line 255 x). These are generic and support reading the bitmap content from outside the library - providing flexibility.

But I also wanted to make it easy to read the bitmap content from the most common locations. Therefore we have the CREATE_FROM_MIME_REPOSITORY, CREATE_FROM_BDS_GRAPHIC, and CREATE_FROM_FRONTEND_UPLOAD methods. These methods contain the necessarily logic to process images from their various sources.  But many of these sources also support graphic formats other than bitmaps.  Internally this class must process everything in bitmaps, but I wanted to support the conversion from other popular formats (like GIFs and JPEGs).  Luckily this functionality already is provided by the IGS and I just reused the existing class, CL_IGS_IMAGE_CONVERTER, to do the processing.

The method CREATE_FROM_EXT_FORMAT provides the logic to interact with the IGS and perform the conversion.  It supports conversions to/from JPEG, TIFF, PNG, GIF, and of course Bitmap. There is one common static method than can be used inside the class and by calling programs named CHECK_IGS_SETUP to test to see if the IGS is present, configured and of a late enough release level to support image conversion.  That way if there is any problem with the IGS setup, exceptions can be thrown gracefully and the alternative image types simply aren't supported.

There are also similar methods for outputting the bitmap image - including outbound format conversion and Zip compression- GET_CONTENT_ZIP_BYTESTREAM, GET_CONTENT_BYTESTREAM, GET_CONTENT_EXT_FORMAT, and GET_CONTENT_BYTETABLE.

Next are the methods that help to display the bitmap in different environments.  First is the method DISPLAY_IN_SAPGUI.  This is designed for Classic Dynpro. You can pass in the hosting container element - or if you want the ZCL_ABAP_BITMAP to control the image, it can provide its own Dialog Container Control.  This was very helpful for debugging, because it makes it possible to test the class and display the results without any wrapper program.

The other display helper method is the PUSH_CONTENT_INTO_ICM_CACHE.  This places the content for the image into the ICM content cache for the specified amount of time and returns the unique URL to this cached object.  This is a great way to manipulate the image, and then display it in an image element using BSP or Web Dynpro ABAP without ever having to permanently store the image.

We have one method, GET_HEADER_INFORMATION, that reads the Bitmap header and returns all the most important information (like width, height, color depth, etc).

Finally we have the really fun methods: the transformations. These are the methods that directly manipulate the Bitmap Content.  The names of the methods do a rather good job of describing exactly what they do to the image: TRANSFORM_ROTATE_CLOCKWISE, TRANSFORM_ROTATE_COUNTER_CLOCK, TRANSFORM_FLIP, TRANSFORM_MIRROR, TRANSFORM_INVERSION, TRANSFORM_GREYSCALE.

Test Applications


No library would really be complete without some nice test and demo applications. For this purpose I built two different test suites.  The first is classic dynpro based.  The application lets you load images from the Frontend.  It displays the results using the DISPLAY_IN_SAPGUI method and has buttons to activate the Transformations.



Just to demonstrate how (hopefully) easy this class is to use, here is the source code of the classic dynpro test application:
REPORT zabap_bitmap_test.

DATA bitmap TYPE REF TO zcl_abap_bitmap.
DATA custom_container TYPE REF TO cl_gui_custom_container.
DATA okcode TYPE syucomm.

START-OF-SELECTION.

CALL SCREEN 100.
*&---------------------------------------------------------------------*
*& Module STATUS_0100 OUTPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE status_0100 OUTPUT.
SET PF-STATUS 'MAIN'.
SET TITLEBAR '100'.
IF custom_container IS INITIAL.
CREATE OBJECT custom_container
EXPORTING
container_name = 'CUSTOM_CONTAINER'.
ENDIF.

IF bitmap IS INITIAL.
TRY.
bitmap = zcl_abap_bitmap=>create_from_frontend_upload( ).
bitmap->display_in_sapgui(
i_container = custom_container ).
CATCH zcx_abap_bitmap .
ENDTRY.
ENDIF.
ENDMODULE. " STATUS_0100 OUTPUT
*&---------------------------------------------------------------------*
*& Module USER_COMMAND_0100 INPUT
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
MODULE user_command_0100 INPUT.
TRY.
CASE okcode.
WHEN 'BACK' OR 'CANCEL' OR 'EXIT'.
LEAVE PROGRAM.
WHEN 'NEW'.
bitmap->free( ).
CLEAR bitmap.
WHEN 'CLOCK'.
bitmap->transform_rotate_clockwise( ).
bitmap->refresh_sapgui_display( ).
WHEN 'COUNTER'.
bitmap->transform_rotate_counter_clock( ).
bitmap->refresh_sapgui_display( ).
WHEN 'GRAY'.
bitmap->transform_greyscale( ).
bitmap->refresh_sapgui_display( ).
WHEN 'INVERSE'.
bitmap->transform_inversion( ).
bitmap->refresh_sapgui_display( ).
WHEN 'MIRROR'.
bitmap->transform_mirror( ).
bitmap->refresh_sapgui_display( ).
WHEN 'FLIP'.
bitmap->transform_flip( ).
bitmap->refresh_sapgui_display( ).
ENDCASE.
CATCH zcx_abap_bitmap .
CLEAR okcode.
ENDTRY.
CLEAR okcode.
ENDMODULE. " USER_COMMAND_0100 INPUT

But the more full featured test application is actually the Web Dynpro ABAP version.  It has all the same functionality to load existing images, display them, and perform transformations:



However it also has functionality to export the manipulated image in several formats (as well as zip compression):


Downloads


Now for the downloads.  I have broken them out into separate parts and pieces so people can grab just what they need.


(!!!Downloadlinks do not work anymore!!!) added by moderator
32 Comments