Friday, 20 August 2010

Delphi XE preview videos

Here you can see the latest videos from embarcadero talking about the new version of Delphi called Delphi XE.
First preview:
this second preview covers some of the third party tools that will be part of Delphi XE:

  • FinalBuilder, used to manage the build process
  • The ability to invoke IDE operations (like audits, metrics, code formatting) from the command line, and so invoke them from external tools like FinalBuilder
  • The integrated version of profiling tool AQTime
  • The logging support provided by CodeSite, a nice tool written by Ray Konopka.

Thursday, 19 August 2010

TBookMark problem with Delphi 2010 and TList

Going on with the big rewrite of code from Delphi 2007 to 2010 (most of it an adaptation more than a rewrite), but we've found some hidden problems as a result of changes appeared in the new version. One of the flaws was shown while I was working with TBookMark class, trying to position a dataset in a given bookmark. With the help of the Embarcadero Forum, we achieved a solution by using "generics" (I'm preparing a post with generics in Delphi 2010) because the TBookMark is now of type TBytes.
Then, the solution would be something similar to this:
uses
    Generics.Collections;

var
    bookmarkList: TList<TBookmark>; //The same as TList<SysUtils.TBytes>

//Adding the bookmark
bookmarkList.Add(cds.GetBookmark);

//We don't need the TBookMark cast
procedure TForm1.GotoBkMarksClick(Sender: TObject);
var
    Cnt: Integer;
begin
    for Cnt := 0 to Pred(BookmarkList.Count) do
    begin
        if DataSet.BookmarkValid(BookmarkList[Cnt]) then
        begin
            DataSet.GotoBookmark(BookmarkList[Cnt]);
            ShowMessage(DataSet.FieldByName('Id').AsString);
       end;
    end;
end;
You need to take into account that every project is different and the use of the TBookMark can differ from one project to another.

Wednesday, 18 August 2010

TIniFile looses unicode characters

We've recently started converting code from Delphi 2007 to Delphi 2010 and we noticed that the TInifile looses unicode characters when we try to save an unicode string into the file. The file is UTF-8 encoding and when we try to save characters like 'ó', 'ç', 'á', etc., they don't appear or some unrecognised characters are shown into the ini file instead of the normal ones.
If the file is ANSI encoded and we try yo use the Tinifile, it will work fine with the unicode characters but not if the ini file is converted to UTF-8.
To solve this, we can use the TMemIniFile class. TMemInifile has an overload for the constructor that allows you to pass the encoding used for the file. The code example to solve this is the following:
procedure TForm1.Button1Click(Sender: TObject);
var
    inifile : TMemIniFile;
    desc : string;
    temp : WideString;
begin
   inifile := TMemIniFile.Create('C:\fileIni.ini', TEncoding.UTF8);
   temp := 'óáç';
   desc := string(temp);
   inifile.WriteString('Section', desc, 'S');
   inifile.UpdateFile;
   inifile.Free;
end;

Tuesday, 17 August 2010

Reading the Exif and IPTC information from a JPEG image with Delphi

Most of you know about my passion with photography and these days I've been working on a new project setting up my own photographic web gallery. I know that I've been more focused on photography than programming, but it's one of my hobbies and after doing some courses I can say that I really enjoy it!. Anyway, going on with my passion, I started the gallery with Flash and XML, and the big problem here was the modification of all the pictures. This tedious task let me build a set of applications to increase speed and productivity while I was uploading the pictures into the web. I started creating Thundax Batch Watermark, a very powerful tool that let you add a watermark to your pictures. Then, few days ago, I released the Thundax Image Resizer to built the resized image with its thumbnail, and finally the Thundax Exif Information to obtain all the Exif (Exchangeable image file format) information contained into the picture with all the information about the camera, exposure time, focal length, etc.
This information is contained into the Resume section of the file:

And I've been struggling in how to get that with Delphi. After a few hours of research, I found the CCR Exif Library for Delphi, a very simple and powerful library for reading Exif and IPTC information. Now with my application we can get the most important information and use it to publish your work:
The library is very easy to use and with a very little lines of code we can extract all the information we want and present it in the desired format.
The code looks like this:
procedure TForm1.ListView1Click(Sender: TObject);
var
  ExifData: TExifData;
  JPEGFile : string;
begin
    if ListView1.ItemIndex = -1 then
        Exit;
    ExifData := nil;
    imgThumbnail.Picture.Assign(nil);
    Memo1.Clear;
    JPEGFile := ListView1.Items[ListView1.ItemIndex].Caption;
    try
        ExifData := TExifData.Create;
        ExifData.EnsureEnumsInRange := False; 
        ExifData.LoadFromJPEG(JPEGFile);
        if ExifData.Empty then
            Memo1.lines.Add('No Exif metadata found')
        else
            LoadStandardValues(ExifData);

      if imgThumbnail.Picture.Graphic <> nil then
      begin
        grpThumbnail.Width := (grpThumbnail.Width - imgThumbnail.Width) +
          imgThumbnail.Picture.Width;
        grpThumbnail.Visible := True;
      end;
    finally
        ExifData.Free;
    end;
end;

Now we can publish our pictures with all the camera information like this:

Camera model: PENTAX K10D       

Date/time: 03/01/2009 17:55:05

Resolution: 72 x 72 inches

Exposure time: 0,3 seconds

F number: F/4

Focal length: 28,13 mm

ISO speed rating(s): 400


You can find more information about this in the following links:

Wednesday, 11 August 2010

Nircmd - Freeware Windows command-line tool

Today one of my workmates told me about the Nircmd command-line tool from NirSoft, and I thought it deserved a post in my blog because its strength to modify from turning off your computer to hiding the start button on the system try.
NirCmd is a small command-line utility that allows you to do some useful tasks without displaying any user interface. By running NirCmd with simple command-line option, you can write and delete values and keys in the Registry, write values into INI file, dial to your internet account or connect to a VPN network, restart windows or shut down the computer, create shortcut to a file, change the created/modified date of a file, change your display settings, turn off your monitor, open the door of your CD-ROM drive, and more... 
  You can download the last version of the application from here.

Resizing a JPEG Image with Thundax Image Resizer

These days I've been focused on a new web gallery for my pictures and I've come up with the idea of developing a simple application using Delphi 2010 to help me with the tedious task of resizing all my pictures in different sizes and creating thumbnails as well. I used and modified the code from Andrew Jameson to smoothly resize a JPEG image. With my last application you can resize an image in 3 different sizes in one shoot, defining them into the program. The application is called Thundax Image Resizer and you can download it for free. It can resize JPEG images and it can do it in batch.

Here you can see an image of the program:
Once the picture is selected and we resize de image, we'll get the new resized images into the output directory with the name concatenated with its resolution.
Afterwards, we can check our pictures resized and keeping a high quality:

Here you can get the code of the unit LibResize.pas:

unit LibResize;

interface

uses
    jpeg, windows, Graphics, SysUtils, Classes, StrUtils;

type
    TRGBArray = array [Word] of TRGBTriple;
    pRGBArray = ^TRGBArray;

procedure ResizeImage(path : string; FileName: string; MaxWidth: Integer; quality : integer);

implementation

procedure SmoothResize(Src, Dst: TBitmap);
var
    x, y: Integer;
    xP, yP: Integer;
    xP2, yP2: Integer;
    SrcLine1, SrcLine2: pRGBArray;
    t3: Integer;
    z, z2, iz2: Integer;
    DstLine: pRGBArray;
    DstGap: Integer;
    w1, w2, w3, w4: Integer;
begin
    Src.PixelFormat := pf24Bit;
    Dst.PixelFormat := pf24Bit;

    if (Src.Width = Dst.Width) and (Src.Height = Dst.Height) then
        Dst.Assign(Src)
    else
    begin
        DstLine := Dst.ScanLine[0];
        DstGap := Integer(Dst.ScanLine[1]) - Integer(DstLine);
        xP2 := MulDiv(pred(Src.Width), $10000, Dst.Width);
        yP2 := MulDiv(pred(Src.Height), $10000, Dst.Height);
        yP := 0;

        for y := 0 to pred(Dst.Height) do
        begin
            xP := 0;
            SrcLine1 := Src.ScanLine[yP shr 16];

            if (yP shr 16 < pred(Src.Height)) then
                SrcLine2 := Src.ScanLine[succ(yP shr 16)]
            else
                SrcLine2 := Src.ScanLine[yP shr 16];

            z2 := succ(yP and $FFFF);
            iz2 := succ((not yP) and $FFFF);
            for x := 0 to pred(Dst.Width) do
            begin
                t3 := xP shr 16;
                z := xP and $FFFF;
                w2 := MulDiv(z, iz2, $10000);
                w1 := iz2 - w2;
                w4 := MulDiv(z, z2, $10000);
                w3 := z2 - w4;
                DstLine[x].rgbtRed := (SrcLine1[t3].rgbtRed * w1 + SrcLine1[t3 + 1].rgbtRed * w2 + SrcLine2[t3].rgbtRed * w3 + SrcLine2[t3 + 1]
                        .rgbtRed * w4) shr 16;
                DstLine[x].rgbtGreen := (SrcLine1[t3].rgbtGreen * w1 + SrcLine1[t3 + 1].rgbtGreen * w2 +
                        SrcLine2[t3].rgbtGreen * w3 + SrcLine2[t3 + 1].rgbtGreen * w4) shr 16;
                DstLine[x].rgbtBlue := (SrcLine1[t3].rgbtBlue * w1 + SrcLine1[t3 + 1].rgbtBlue * w2 + SrcLine2[t3].rgbtBlue * w3 + SrcLine2[t3 + 1]
                        .rgbtBlue * w4) shr 16;
                Inc(xP, xP2);
            end;
            Inc(yP, yP2);
            DstLine := pRGBArray(Integer(DstLine) + DstGap);
        end;
    end;
end;

function LoadJPEGPictureFile(Bitmap: TBitmap; FilePath, FileName: string): Boolean;
var
    JPEGImage: TJPEGImage;
begin
    if (FileName = '') then
        Result := False
    else
    begin
        try
            JPEGImage := TJPEGImage.Create;
            try
                JPEGImage.LoadFromFile(FilePath + FileName);
                Bitmap.Assign(JPEGImage);
                Result := true;
            finally
                JPEGImage.Free;
            end;
        except
            Result := False;
        end;
    end;
end;

function SaveJPEGPictureFile(Bitmap: TBitmap; FilePath, FileName: string; Quality: Integer): Boolean;
var
    size: string;
    extension: string;
    newName: string;
begin
    Result := true;
    try
        if ForceDirectories(FilePath) then
        begin
            with TJPEGImage.Create do
            begin
                try
                    Assign(Bitmap);
                    CompressionQuality := Quality;
                    size := '_' + IntToStr(Bitmap.Width) + 'x' + IntToStr(Bitmap.Height);
                    extension := ExtractFileExt(FileName);
                    newName := AnsiLeftStr(FileName, Length(FileName) - Length(extension)) + size + extension;
                    SaveToFile(FilePath + newName);
                finally
                    Free;
                end;
            end;
        end;
    except
        raise ;
        Result := False;
    end;
end;

function JPEGDimensions(FileName: string; var x, y: Word): Boolean;
var
    SegmentPos: Integer;
    SOIcount: Integer;
    b: byte;
begin
    Result := False;
    with TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone) do
    begin
        try
            Position := 0;
            Read(x, 2);
            if (x <> $D8FF) then
                exit;
            SOIcount := 0;
            Position := 0;
            while (Position + 7 < size) do
            begin
                Read(b, 1);
                if (b = $FF) then
                begin
                    Read(b, 1);
                    if (b = $D8) then
                        Inc(SOIcount);
                    if (b = $DA) then
                        break;
                end;
            end;
            if (b <> $DA) then
                exit;
            SegmentPos := -1;
            Position := 0;
            while (Position + 7 < size) do
            begin
                Read(b, 1);
                if (b = $FF) then
                begin
                    Read(b, 1);
                    if (b in [$C0, $C1, $C2]) then
                    begin
                        SegmentPos := Position;
                        dec(SOIcount);
                        if (SOIcount = 0) then
                            break;
                    end;
                end;
            end;
            if (SegmentPos = -1) then
                exit;
            if (Position + 7 > size) then
                exit;
            Position := SegmentPos + 3;
            Read(y, 2);
            Read(x, 2);
            x := Swap(x);
            y := Swap(y);
            Result := true;
        finally
            Free;
        end;
    end;
end;

procedure ResizeImage(path : string; FileName: string; MaxWidth: Integer; quality : integer);
var
    OldBitmap: TBitmap;
    NewBitmap: TBitmap;
begin
    OldBitmap := TBitmap.Create;
    try
        if LoadJPEGPictureFile(OldBitmap, ExtractFilePath(FileName), ExtractFileName(FileName)) then
        begin
            if (OldBitmap.Width > MaxWidth) then
            begin
                NewBitmap := TBitmap.Create;
                try
                    NewBitmap.Width := MaxWidth;
                    NewBitmap.Height := MulDiv(MaxWidth, OldBitmap.Height, OldBitmap.Width);
                    SmoothResize(OldBitmap, NewBitmap);
                    SaveJPEGPictureFile(NewBitmap, path, ExtractFileName(FileName), quality)
                finally
                    NewBitmap.Free;
                end;
            end;
        end;
    finally
        OldBitmap.Free;
    end;
end;

end.

Friday, 6 August 2010

Monday, 2 August 2010

A headset that reads your brainwaves

Today I bring you an incredible presentation of Tan Le, the head of Emotiv Systems, which is developing the next generation of human-machine interface -- a headset that takes input directly from the brain. This new computer interface reads its user's brainwaves, making it possible to control virtual objects, and even physical electronics, with mere thoughts (and a little concentration). She demos the headset, and talks about its far-reaching applications.
I hope you enjoy the video.
Source: TED (Ideas worth spreading)