MovePelican

The purpose of program MovePelican is to copy a pelican from a photograph with the blue background of the sky to the unfamiliar setting of the Thames. The bitmaps (.bmp files) used here use 24 bits per pixel. They use one byte each for the red, green and blue intensities of each pixel; there is no colour table. After a 54 byte header, the bytes for each row are given in the order blue, green, and then red. The first row of data corresponds to the bottom row of the graphic. The format requires that padding bytes are included to keep each row to a multiple of 32 bits. For convenience, all three graphics have same width and height. The code copies pixels that are not in shades of background blue to the target file. The two starting images and the result are shown below.

You need a copy of pelican.bmp and london_eye.bmp in your program folder. We obtained both images (the-london-eye-and-the-aquarium_w725_h544 and pelicans-wings-flying_w725_h544) from Public-Domain-Image.com, but had to change the colour of pixels close to the pelican to create pelican.bmp in order to obtain a satisfactory result.

program MovePelican;
  {$APPTYPE CONSOLE}
uses
  SysUtils;
var
  Background, Foreground : file of byte;
  R, G, B, Dummy : byte;
  Start, Row, Col, Width, Height, RowDataMod4, PaddingWidth,
  i, FileSize: integer;

function ReadInt(RecNo : integer) : integer;
var
  Byte1, Byte2, Byte3, Byte4 : byte;
begin
  seek(Foreground, RecNo);
  read(Foreground, Byte1);
  read(Foreground, Byte2);
  read(Foreground, Byte3);
  read(Foreground, Byte4);
  {Four bytes of integer stored 'little endian' (with least significant byte
   first)}
  result := Byte1 + Byte2 * 256 + Byte3 * 256 * 256 + Byte4 * 256 * 256 * 256;
end;

begin
  assignfile(Background, 'london_eye.bmp');
  assignfile(Foreground, 'pelican.bmp');
  reset(Background);
  reset(ForeGround);
  Start := ReadInt(10);
  writeln('Start: ', Start);
  Width := ReadInt(18);
  writeln('Width: ', Width);
  RowDataMod4 := (Width * 3) MOD 4;
  if RowDataMod4 = 0 then
    begin
      PaddingWidth := 0;
    end
  else
    begin
      PaddingWidth := 4 - RowDataMod4;
    end;
  writeln('PaddingWidth: ', PaddingWidth);
  Height := ReadInt(22);
  writeln('Height: ', Height);
  FileSize := (Width * 3 + PaddingWidth) * Height + 54;
  writeln('File size: ', FileSize);
  //Move file pointers to start of data then copy required pixels
  seek(Background, 54);
  seek(Foreground, 54);
  for Row := Height downto 1 do
    begin
      for Col := 1 to Width do
        begin
          read(Foreground, B);
          read(Foreground, G);
          read(Foreground, R);
          //Do not copy these shades of blue
          if (R > 60) and (R < 160) and (G > 90) and (G < 180)
                              and (B > 150) and (B < 220) then
             begin  //Move the target file pointer 3 places
              read(Background, Dummy);
              read(Background, Dummy);
              read(Background, Dummy);
             end
          else    //Write byte of pelican
            begin
              write(Background, B);
              write(Background, G);
              write(Background, R);
            end;
        end; //Col
        if PaddingWidth > 0 then
          begin
            //Advance the pointer past the padding bytes in both files.
            for i := 1 to PaddingWidth do
              begin
                read(ForeGround, Dummy);
                read(Background, Dummy);
              end;
          end;
    end;  //Row
  closefile(Background);
  closefile(Foreground);
  readln;
end.
Programming - a skill for life!

Changing colours, copying images and hiding data