zoukankan      html  css  js  c++  java
  • 二维码生成

    http://zarko-gajic.iz.hr/firemonkey-mobile-android-ios-qr-code-generation-using-delphi-xe-5-delphizxingqrcode/

    FireMonkey / Mobile (Android, iOS) QR Code Generation Using Delphi XE 5 / DelphiZXingQRCode

    fmx-TDelphiZXingQRCode The DelphiZXingQRCode unit, ported from ZXing by Debenu, allows to easily add QR Code generation to your Delphi VCL applications.

    While the DelphiZXingQRCode was designed to support VCL applications (and be used in newer as well as in older Delphi versions), it can also easily be “upgraded” for Fire Monkey applications – be it desktop or mobile (Android, iOS).

    DelphiZXingQRCode -> FireMonkey Support

    To FireMonkey-enable the DelphiZXingQRCode unit a few changes to the source code were required.

    The unit uses “contnrs” (System.Contnrs.pas) unit which implements TObjectList (used by the unit) and other container like classes. Under FireMonkey for mobile,  more specifically under ARC, the classes contained in System.Contnrs.pas cannot be used because they are not ARC compliant.

    Equivalent classes are to be found in System.Generics.Collections.pas (ARC compliant) where generics versions of TObjectList is defined.

    Therefore, the first change is to replace “uses contnrs, …” with “uses System.Generics.Collections, …”

    This also requires to make changes like: from “Blocks: TObjectList;” to “Blocks: TObjectList<TBlockPair>;” – that is to use strongly typed generics list classes.

    Further, there are lots of “ansistring” and “widestring” types used for parameters in various functions inside the unit. If you want to go mobile, those are not supported and you should use “string”. More info here: Migrating Delphi Code to Mobile from Desktop.

    Therefore, the second change to the unit would be to replace “widestring” with “string” and “ansistring” with “array of byte” (or something else as explained in the article).

    Single Pixel Drawing (and Other Canvas Drawing) FireMonkey Style

    In the VCL, the TCanvas class allows accessing single pixels through the “Canvas.Pixels()” property. In FireMonkey this is not supported and you have to use the SetPixel property of a TBitmapData instance.

    Once the qr code is generated, to convert it to a bitmap image, in FireMonkey:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    var
      QRCode: TDelphiZXingQRCode;
      Row, Column: Integer;
      pixelColor : TAlphaColor;
      vBitMapData : TBitmapData;
      rSrc, rDest : TRectF;
      s : widestring;
    begin
      QRCode := TDelphiZXingQRCode.Create;
      try
        QRCode.Data := edtText.Text;
        QRCode.Encoding := TQRCodeEncoding(cmbEncoding.ItemIndex);
        QRCode.QuietZone := StrToIntDef(edtQuietZone.Text, 4);
        QRCodeBitmap.SetSize(QRCode.Rows, QRCode.Columns);
        for Row := 0 to QRCode.Rows - 1 do
        begin
          for Column := 0 to QRCode.Columns - 1 do
          begin
            if (QRCode.IsBlack[Row, Column]) then
              pixelColor := TAlphaColors.Black
            else
              pixelColor := TAlphaColors.White;
     
            if QRCodeBitmap.Map(TMapAccess.maWrite, vBitMapData)  then
            try
              vBitMapData.SetPixel(Column, Row, pixelColor);
            finally
              QRCodeBitmap.Unmap(vBitMapData);
            end;
          end;
        end;
      finally
        QRCode.Free;
      end;
     
      //refresh image control imgQRCode is a TImage
      {code below}
    end;

    Note: compare this with the VCL approach of TDelphiZXingQRCode usage.

    VCL’s Canvas.StretchDraw to FMX’s Canvas.DrawBitmap

    There’s no StrecthDraw in FMX’s Canvas class. There’s DrawBitmap. StretchDraw by default does no antialiasing. FMX does. So I had to play a little until I was able to make it work correctly.

    I’ve decided to use TImage and not TPaintBox as I was simply not able to make TPictureBox in FireMonkey draw what I wanted :(

    Using TImage, the code to get/display the generated QR Code is as follows:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //refresh image. imgQRCode is a TImage
     
    imgQRCode.Bitmap.SetSize(QRCodeBitmap.Width, QRCodeBitmap.Height);
     
    rSrc := TRectF.Create(0, 0, QRCodeBitmap.Width, QRCodeBitmap.Height);
    rDest := TRectF.Create(0, 0, imgQRCode.Bitmap.Width, imgQRCode.Bitmap.Height);
     
    if imgQRCode.Bitmap.Canvas.BeginScene then
    try
      imgQRCode.Bitmap.Canvas.Clear(TAlphaColors.White);
     
      imgQRCode.Bitmap.Canvas.DrawBitmap(QRCodeBitmap, rSrc, rDest, 1);
    finally
      imgQRCode.Bitmap.Canvas.EndScene;
    end;

    Aliasing – Do Not Need It!

    By default, in FMX, when using DrawBitmap to resize it, antialiasing is used by default. There are two properties you need to set to ensure a bigger copy of your (small) qr code is drawn pixel-copy-perfect.

    1
    2
    3
    4
    begin
      imgQRCode.DisableInterpolation := true;
      imgQRCode.WrapMode := TImageWrapMode.iwStretch;
    end;

    In a FireMonkey Desktop application, this works as expected.

    On FireMonkey Mobile it does not :(  – the resized image is still blurred (antialiasing used).

    That’s it. QR Codes generation in FireMonkey – both desktop and mobile!

    Download the FMX version of DelphiZXIngQRCode.pas: FMX-DelphiZXIngQRCode

    Comments are welcome (even more if you know how to resize a bitmap in FireMonkey on mobile without antialiasing)!

     

    This entry was posted in delphi and tagged , , on December 27, 2013 by .

    Post navigation

    12 thoughts on “FireMonkey / Mobile (Android, iOS) QR Code Generation Using Delphi XE 5 / DelphiZXingQRCode”

    1. Oliver December 28, 2013 at 12:53

      There are two major problems with that approach:

      Firstly, using a Map-Operation in the inner loop, which will be executed for each pixel. Since Firemonkey (ab)uses Textures for Bitmap Canvas,  very costly copy operations will take place. And not only the pixel to be changed, but the whole image is copied back and forth.

      Would be better trying with TSurfaceBitmap instead.

      And second, the mentioned “forced” antialiasing which causes images to look ugly on mobile, which could also lead to the resulting QRCode becoming unreadable eventually.

       
    2. Panagiotis December 30, 2013 at 04:07

      Try the following, not tested on mobile but I believe it will work:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      procedure TForm1.Update;
      const
        downsizeQuality: Integer = 2; // bigger value, better quality, slower rendering
      var
        QRCode: TDelphiZXingQRCode;
        Row, Column: Integer;
        pixelColor : TAlphaColor;
        vBitMapData : TBitmapData;
        pixelCount, y, x: Integer;
        columnPixel, rowPixel: Integer;
        function GetPixelCount(AWidth, AHeight: Single): Integer;
        begin
          if QRCode.Rows > 0 then
            Result := Trunc(Min(AWidth, AHeight)) div QRCode.Rows
          else
            Result := 0;
        end;
      begin
        QRCode := TDelphiZXingQRCode.Create;
        try
          QRCode.Data := edtText.Text;
          QRCode.Encoding := TQRCodeEncoding(cmbEncoding.ItemIndex);
          QRCode.QuietZone := StrToIntDef(edtQuietZone.Text, 4);
          pixelCount := GetPixelCount(imgQRCode.Width, imgQRCode.Height);
          case imgQRCode.WrapMode of
            TImageWrapMode.iwOriginal,TImageWrapMode.iwTile,TImageWrapMode.iwCenter:
            begin
              if pixelCount > 0 then
                imgQRCode.Bitmap.SetSize(QRCode.Columns * pixelCount,
                  QRCode.Rows * pixelCount);
            end;
            TImageWrapMode.iwFit:
            begin
              if pixelCount > 0 then
              begin
                imgQRCode.Bitmap.SetSize(QRCode.Columns * pixelCount * downsizeQuality,
                  QRCode.Rows * pixelCount * downsizeQuality);
                pixelCount := pixelCount * downsizeQuality;
              end;
            end;
            TImageWrapMode.iwStretch:
              raise Exception.Create('Not a good idea to stretch the QR Code');
          end;
          if imgQRCode.Bitmap.Canvas.BeginScene then
          begin
            try
              imgQRCode.Bitmap.Canvas.Clear(TAlphaColors.White);
              if pixelCount > 0 then
              begin
                if imgQRCode.Bitmap.Map(TMapAccess.maWrite, vBitMapData)  then
                begin
                  try
                    for Row := 0 to QRCode.Rows - 1 do
                    begin
                      for Column := 0 to QRCode.Columns - 1 do
                      begin
                        if (QRCode.IsBlack[Row, Column]) then
                          pixelColor := TAlphaColors.Black
                        else
                          pixelColor := TAlphaColors.White;
                        columnPixel := Column * pixelCount;
                        rowPixel := Row * pixelCount;
                        for x := 0 to pixelCount - 1 do
                          for y := 0 to pixelCount - 1 do
                            vBitMapData.SetPixel(columnPixel + x,
                              rowPixel + y, pixelColor);
                      end;
                    end;
                  finally
                    imgQRCode.Bitmap.Unmap(vBitMapData);
                  end;
                end;
              end;
            finally
              imgQRCode.Bitmap.Canvas.EndScene;
            end;
          end;
        finally
          QRCode.Free;
        end;
      end;
       
    3. Willian Santos December 30, 2013 at 11:40

      Congratulations for the article, I needed that.

       
    4. Panagiotis December 30, 2013 at 17:52

      “>” is greater than in my code.

       
      1. zarkogajic Post authorDecember 30, 2013 at 18:01

        fixed, thanks for noticing.

         
        1. Nikos January 9, 2014 at 13:39

          Hello, If I change AnsiString for UTF8Version variable then i cannot set to print  greek letters see bellow procedure TEncoder.Append8BitBytes(const Content: String; Bits: TBitArray; EncodeOptions: Integer); var   Bytes: TByteArray;   I: Integer;   UTF8Version: AnsiString; Is there any suggestion? thanks

           
    5. Pingback: Generate QR Codes Using Firemonkey In Dephi XE5 On IOS And Android | Delphi Firemonkey, Delphi Android, Delphi IOS

    6. Luke February 24, 2014 at 20:31

      Hi, Ive got some following trouble:

      [DCC Error] DelphiZXIngQRCode.pas(242): E2029 Identifier expected but ‘ARRAY’ found   [DCC Error] DelphiZXIngQRCode.pas(854): E2029 Identifier expected but ‘ARRAY’ found   [DCC Error] DelphiZXIngQRCode.pas(860): E2010 Incompatible types: ‘Byte’ and ‘string’ [DCC Error] DelphiZXIngQRCode.pas(870): E2008 Incompatible types   [DCC Error] DelphiZXIngQRCode.pas(1632): E2010 Incompatible types: ‘Dynamic array’ and ‘AnsiString’ [DCC Error] DelphiZXIngQRCode.pas(1642): E2010 Incompatible types: ‘Dynamic array’ and ‘AnsiString’ [DCC Error] DelphiZXIngQRCode.pas(3018): E2010 Incompatible types: ‘TBlockPair’ and ‘TGenericGFPoly’ [DCC Error] DelphiZXIngQRCode.pas(3038): E2010 Incompatible types: ‘TBlockPair’ and ‘TGenericGFPoly’

      Let me know, what could I do. Thanks.

       
      1. zarkogajic Post authorFebruary 25, 2014 at 08:23

        Like: FireMonkey or VCL application?

         
        1. Luke February 25, 2014 at 18:00

          it is XE5 native android.

           
          1. zarkogajic Post authorFebruary 28, 2014 at 15:05

            @Luke: I’ve sent the modified pas file to your email…

             
    7. Luke March 3, 2014 at 14:40

      wohooo…thanks a lot, man!

       

    Comments are closed.

  • 相关阅读:
    《冒号课堂》学习笔记 OOP-继承
    《冒号课堂》学习笔记 编程范式汇总
    EF中主表和附表一起提交的话,如果主附表的主键外键已经设定。
    超时时间已到。在操作完成之前超时时间已过或服务器未响应。 解决方法
    wpf下拉框不能多选的原因
    查询中无法构造实体或复杂类型
    wpf新增记录时用多线程的问题
    面向对象
    HTML5入门以及新标签
    前端学习本地存储
  • 原文地址:https://www.cnblogs.com/key-ok/p/4070560.html
Copyright © 2011-2022 走看看