diff --git a/components/lazbarcodes/examples/qrcodegenerator/fpwritegif.pas b/components/lazbarcodes/examples/qrcodegenerator/fpwritegif.pas new file mode 100644 index 000000000..181791936 --- /dev/null +++ b/components/lazbarcodes/examples/qrcodegenerator/fpwritegif.pas @@ -0,0 +1,846 @@ +{ +Copyright (c) 2007-2023, Udo Schmal + +Permission to use, copy, modify, and/or distribute the software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +https://www.gocher.me/FPWriteGIF +} + +unit FPWriteGIF; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FPImage, FPReadGif; + +type TColor = -$7FFFFFFF - 1..$7FFFFFFF; + +const +// GIF record separators + kGifImageSeparator: byte = $2c; + kGifExtensionSeparator: byte = $21; + kGifTerminator: byte = $3b; + kGifLabelGraphic: byte = $f9; + kGifBlockTerminator: byte = $00; +// LZW encode table sizes + kGifCodeTableSize = 4096; +// Raw rgb value + clNone = TColor($1FFFFFFF); + AlphaOpaque = $FF; + AlphaTransparent = 0; + MaxArr = (MaxLongint div Sizeof(integer)) - 1; + +type + APixel8 = array[0..MaxArr] of Byte; + PAPixel8 = ^APixel8; + + TRGBQuadArray256 = array[0..256] of TFPCompactImgRGBA8BitValue; + TOpenColorTableArray = array of TColor; + TColorTableArray = array[0..$FF] of TColor; + + TOctreeNode = class; // Forward definition so TReducibleNodes can be declared + TReducibleNodes = array[0..7] of TOctreeNode; + TOctreeNode = class(TObject) + IsLeaf: Boolean; + PixelCount: Integer; + RedSum, GreenSum, BlueSum: Integer; + Next: TOctreeNode; + Child: TReducibleNodes; + constructor Create(const Level: Integer; var LeafCount: Integer; var ReducibleNodes: TReducibleNodes); + destructor Destroy; override; + end; + + TFPWriterGIF = class(TFPCustomImageWriter) + private + fHeader: TGifHeader; + fDescriptor: TGifImageDescriptor; // only one image supported + fGraphicsCtrlExt: TGifGraphicsControlExtension; + fTransparent: Boolean; + fBackground: TColor; + fPixels: PAPixel8; + fPixelList: PChar; // decoded pixel indices + fPixelCount: longint; // number of pixels + fColorTable: TColorTableArray; + fColorTableSize: integer; + + procedure SaveToStream(Destination: TStream); + protected + procedure InternalWrite(Stream: TStream; Img: TFPCustomImage); override; + public + constructor Create; override; + destructor Destroy; override; + end; + +implementation +{$REGION ' - TOctreeNode - '} +constructor TOctreeNode.Create(const Level: Integer; var LeafCount: Integer; var ReducibleNodes: TReducibleNodes); +var i: Integer; +begin + PixelCount := 0; + RedSum := 0; + GreenSum := 0; + BlueSum := 0; + for i := Low(Child) to High(Child) do + Child[i] := nil; + IsLeaf := (Level = 8); + if IsLeaf then + begin + Next := nil; + Inc(LeafCount); + end + else + begin + Next := ReducibleNodes[Level]; + ReducibleNodes[Level] := Self; + end +end; + +destructor TOctreeNode.Destroy; +var i: Integer; +begin + for i := Low(Child) to High(Child) do + Child[i].Free +end; +{$ENDREGION} + +{$REGION ' - TFPWriterGIF. - '} +constructor TFPWriterGIF.Create; +begin + inherited Create; +end; + +destructor TFPWriterGIF.Destroy; +begin + inherited Destroy; +end; + +// save the current GIF definition to a stream object +// at first, just write it to our memory stream fSOURCE +procedure TFPWriterGIF.SaveToStream(Destination: TStream); +var + LZWStream: TMemoryStream; // temp storage for LZW + LZWSize: integer; // LZW minimum code size + + // these LZW encode routines sqrunch a bitmap into a memory stream + procedure LZWEncode(); + var + rPrefix: array[0..kGifCodeTableSize-1] of integer; // string prefixes + rSuffix: array[0..kGifCodeTableSize-1] of integer; // string suffixes + rCodeStack: array[0..kGifCodeTableSize-1] of byte; // encoded pixels + rSP: integer; // pointer into CodeStack + rClearCode: integer; // reset decode params + rEndCode: integer; // last code in input stream + rCurSize: integer; // current code size + rBitString: integer; // steady stream of bits to be decoded + rBits: integer; // number of valid bits in BitString + rMaxVal: boolean; // max code value found? + rCurX: integer; // position of next pixel + rCurY: integer; // position of next pixel + rCurPass: integer; // pixel line pass 1..4 + rFirstSlot: integer; // for encoding an image + rNextSlot: integer; // for encoding + rCount: integer; // number of bytes read/written + rLast: integer; // last byte read in + rUnget: boolean; // read a new byte, or use zLast? + + procedure LZWReset; + var i: integer; + begin + for i := 0 to (kGifCodeTableSize - 1) do + begin + rPrefix[i] := 0; + rSuffix[i] := 0; + end; + rCurSize := LZWSize + 1; + rClearCode := (1 shl LZWSize); + rEndCode := rClearCode + 1; + rFirstSlot := (1 shl (rCurSize - 1)) + 2; + rNextSlot := rFirstSlot; + rMaxVal := false; + end; + + // save a code value on the code stack + procedure LZWSaveCode(Code: integer); + begin + rCodeStack[rSP] := Code; + inc(rSP); + end; + + // save the code in the output data stream + procedure LZWPutCode(code: integer); + var + n: integer; + b: byte; + begin + // write out finished bytes + // a literal "8" for 8 bits per byte + while (rBits >= 8) do + begin + b := (rBitString and $ff); + rBitString := (rBitString shr 8); + rBits := rBits - 8; + LZWStream.Write(b, 1); + end; + // make sure no junk bits left above the first byte + rBitString := (rBitString and $ff); + // and save out-going code + n := (code shl rBits); + rBitString := (rBitString or n); + rBits := rBits + rCurSize; + end; + + // get the next pixel from the bitmap, and return it as an index into the colormap + function LZWReadBitmap: integer; + var + n: integer; + j: longint; + p: PChar; + begin + if (rUnget) then + begin + n := rLast; + rUnget := false; + end + else + begin + inc(rCount); + j := (rCurY * fDescriptor.Width) + rCurX; + if ((0 <= j) and (j < fPixelCount)) then + begin + p := fPixelList + j; + n := ord(p^); + end + else + n := 0; + // if first pass, make sure CurPass was initialized + if (rCurPass = 0) then rCurPass := 1; + inc(rCurX); // inc X position + if (rCurX >= fDescriptor.Width) then // bumping Y ? + begin + rCurX := 0; + inc(rCurY); + end; + end; + rLast := n; + result := n; + end; + + var + i,n, + cc: integer; // current code to translate + oc: integer; // last code encoded + found: boolean; // decoded string in prefix table? + pixel: byte; // lowest code to search for + ldx: integer; // last index found + fdx: integer; // current index found + b: byte; + begin + // init data block + fillchar(rCodeStack, sizeof(rCodeStack), 0); + rBitString := 0; + rBits := 0; + rCurX := 0; + rCurY := 0; + rCurPass := 0; + rLast := 0; + rUnget:= false; + + LZWReset; + // all within the data record + // always save the clear code first ... + LZWPutCode(rClearCode); + // and first pixel + oc := LZWReadBitmap; + LZWPutCode(oc); + // nothing found yet (but then, we haven't searched) + ldx := 0; + fdx := 0; + // and the rest of the pixels + rCount := 1; + while (rCount <= fPixelCount) do + begin + rSP := 0; // empty the stack of old data + n := LZWReadBitmap; // next pixel from the bitmap + LZWSaveCode(n); + cc := rCodeStack[0]; // beginning of the string + // add new encode table entry + rPrefix[rNextSlot] := oc; + rSuffix[rNextSlot] := cc; + inc(rNextSlot); + if (rNextSlot >= kGifCodeTableSize) then + rMaxVal := true + else if (rNextSlot > (1 shl rCurSize)) then + inc(rCurSize); + // find the running string of matching codes + ldx := cc; + found := true; + while (found and (rCount <= fPixelCount)) do + begin + n := LZWReadBitmap; + LZWSaveCode(n); + cc := rCodeStack[0]; + if (ldx < rFirstSlot) then + i := rFirstSlot + else + i := ldx + 1; + pixel := rCodeStack[rSP - 1]; + found := false; + while ((not found) and (i < rNextSlot)) do + begin + found := ((rPrefix[i] = ldx) and (rSuffix[i] = pixel)); + inc(i); + end; + if (found) then + begin + ldx := i - 1; + fdx := i - 1; + end; + end; + // if not found, save this index, and get the same code again + if (not found) then + begin + rUnget := true; + rLast := rCodeStack[rSP-1]; + dec(rSP); + cc := ldx; + end + else + cc := fdx; + // whatever we got, write it out as current table entry + LZWPutCode(cc); + if ((rMaxVal) and (rCount <= fPixelCount)) then + begin + LZWPutCode(rClearCode); + LZWReset; + cc := LZWReadBitmap; + LZWPutCode(cc); + end; + oc := cc; + end; + LZWPutCode(rEndCode); + // write out the rest of the bit string + while (rBits > 0) do + begin + b := (rBitString and $ff); + rBitString := (rBitString shr 8); + rBits := rBits - 8; + LZWStream.Write(b, 1); + end; + end; + +var i: integer; +begin + Destination.Position := 0; + with fHeader do + begin + // write the GIF signature + // if only one image, and no image extensions, then GIF is GIF87a, + // else use the updated version GIF98a + // we just added an extension block; the signature must be version 89a + Destination.Write(Signature, 3); + Destination.Write(Version, 3); + // write the overall GIF screen description to the source stream + Destination.Write(ScreenWidth, 2); // logical screen width + Destination.Write(ScreenHeight, 2); // logical screen height + Destination.Write(Packedbit, 1); // packed bit fields (Global Color valid, Global Color size, Sorted, Color Resolution) + Destination.Write(BackgroundColor, 1); // background color + Destination.Write(AspectRatio, 1); // pixel aspect ratio + if (Packedbit and $80)>0 then //Global Color valid + // write out color gobal table with RGB values + for i := 0 to fColorTableSize-1 do + Destination.Write(fColorTable[i], 3); + end; + // write out graphic extension for this image + Destination.Write(kGifExtensionSeparator, 1); // write the extension separator + Destination.Write(kGifLabelGraphic, 1); // write the extension label + Destination.Write(fGraphicsCtrlExt.BlockSize, 1); // block size (always 4) + Destination.Write(fGraphicsCtrlExt.Packedbit, 1); // packed bit field + Destination.Write(fGraphicsCtrlExt.DelayTime, 2); // delay time + Destination.Write(fGraphicsCtrlExt.ColorIndex, 1); // transparent color + Destination.Write(fGraphicsCtrlExt.Terminator, 1); // block terminator + // write actual image data + Destination.Write(kGifImageSeparator, 1); + // write the next image descriptor shortcut to the record fields + with fDescriptor do + begin + // write the basic descriptor record + Destination.Write(Left, 2); // left position + Destination.Write(Top, 2); // top position + Destination.Write(Width, 2); // size of image + Destination.Write(Height, 2); // size of image + Destination.Write(Packedbit, 1); // packed bit field + // there is no local color table defined we use global + LZWSize := 8; // the LZW minimum code size + Destination.Write(LZWSize, 1); + LZWStream := TMemoryStream.Create; // init the storage for compressed data + try + LZWEncode(); // encode the image and save it in LZWStream + // write out the data stream as a series of data blocks + LZWStream.Position := 0; + while (LZWStream.Position < LZWStream.Size) do + begin + i := LZWStream.Size - LZWStream.Position; + if (i > 255) then i := 255; + Destination.Write(i, 1); + Destination.CopyFrom(LZWStream, i); + end; + finally + FreeAndNil(LZWStream); + end; + Destination.Write(kGifBlockTerminator, 1); // block terminator + end; + Destination.Write(kGifTerminator, 1); // done with writing +end; + +procedure TFPWriterGIF.InternalWrite(Stream: TStream; Img: TFPCustomImage); +var + CT: TOpenColorTableArray; + Palette: TList; + PaletteHasAllColours: Boolean; + Mappings: array[BYTE, BYTE] of TList; + Tree: TOctreeNode; + LeafCount: Integer; + ReducibleNodes: TReducibleNodes; + LastColor: TColor; + LastColorIndex: Byte; + + // convert TFPCustomImage TFPColor to TColor + function FPColorToTColor(const FPColor: TFPColor): TColor; + begin + result := TColor(((FPColor.Red shr 8) and $ff) or (FPColor.Green and $ff00) or ((FPColor.Blue shl 8) and $ff0000)); + end; + + // try to make color table of all colors + function MakeColorTableOfAllColors(): Boolean; + var + Flags: array[Byte, Byte] of TBits; + x, y, ci: Cardinal; + Red, Green, Blue: Byte; + Cnt: word; + begin + result := false; + // init Flags + for y := 0 to $FF do + for x := 0 to $FF do + Flags[x, y] := nil; + try + for ci := 0 to $ff do + CT[ci] := 0; + Cnt := 0; + for y := 0 to Img.Height - 1 do + for x := 0 to Img.Width - 1 do + begin + Red := Byte(Img.Colors[x, y].red shr 8); + Green := Byte(Img.Colors[x, y].green shr 8); + Blue := Byte(Img.Colors[x, y].blue shr 8); + if (Flags[Red, Green]) = nil then + begin + Flags[Red, Green] := Classes.TBits.Create; + Flags[Red, Green].Size := 256; + end; + if not Flags[Red, Green].Bits[Blue] then + begin + CT[Cnt] := FPColorToTColor(Img.Colors[x, y]); + if Cnt = $ff then exit; + inc(Cnt); + Flags[Red, Green].Bits[Blue] := true; + end; + end; + result := true; + PaletteHasAllColours := true; + finally // free Flags + for y := 0 to $FF do + for x := 0 to $FF do + if Flags[x, y] <> nil then + FreeAndNil(Flags[x, y]); + end; + fColorTableSize := High(CT) + 1; + for x := 0 to fColorTableSize - 1 do + fColorTable[x] := CT[x]; + LastColor := clNone; + end; + + procedure MakeColorTableofReducedColors(); + procedure AddColor(var Node: TOctreeNode; const r, g, b: Byte; const Level: Integer; var ReducibleNodes: TReducibleNodes); + const mask: array[0..7] of Byte = ($80, $40, $20, $10, $08, $04, $02, $01); + var Index, Shift: Integer; + begin + if Node = nil then + Node := TOctreeNode.Create(Level, LeafCount, ReducibleNodes); + if Node.IsLeaf then + begin + Inc(Node.PixelCount); + Inc(Node.RedSum, r); + Inc(Node.GreenSum, g); + Inc(Node.BlueSum, b) + end + else + begin + Shift := 7 - Level; + Index := (((r and mask[Level]) shr Shift) shl 2) or (((g and mask[Level]) shr Shift) shl 1) or + ((b and mask[Level]) shr Shift); + AddColor(Node.Child[Index], r, g, b, Level + 1, ReducibleNodes) + end + end; + + procedure ReduceTree(var LeafCount: Integer; var ReducibleNodes: TReducibleNodes); + var + RedSum, BlueSum, GreenSum, Children, i: Integer; + Node: TOctreeNode; + begin + i := 7; + while (i > 0) and (ReducibleNodes[i] = nil) do + dec(i); + Node := ReducibleNodes[i]; + ReducibleNodes[i] := Node.Next; + RedSum := 0; + GreenSum := 0; + BlueSum := 0; + Children := 0; + for i := Low(ReducibleNodes) to High(ReducibleNodes) do + if Node.Child[i] <> nil then + begin + Inc(RedSum, Node.Child[i].RedSum); + Inc(GreenSum, Node.Child[i].GreenSum); + Inc(BlueSum, Node.Child[i].BlueSum); + Inc(Node.PixelCount, Node.Child[i].PixelCount); + Node.Child[i].Free; + Node.Child[i] := nil; + inc(Children) + end; + Node.IsLeaf := true; + Node.RedSum := RedSum; + Node.GreenSum := GreenSum; + Node.BlueSum := BlueSum; + Dec(LeafCount, Children - 1) + end; + + procedure GetPaletteColors(const Node: TOctreeNode; var RGBQuadArray: TRGBQuadArray256; var Index: integer); + var i: integer; + begin + if Node.IsLeaf then + begin + with RGBQuadArray[Index] do + begin + try + r := Byte(Node.RedSum div Node.PixelCount); + g := Byte(Node.GreenSum div Node.PixelCount); + b := Byte(Node.BlueSum div Node.PixelCount); + a := 0; + except + r := 0; + g := 0; + b := 0; + a := 0; + end; + a := 0 + end; + inc(Index); + end + else + for i := Low(Node.Child) to High(Node.Child) do + if Node.Child[i] <> nil then + GetPaletteColors(Node.Child[i], RGBQuadArray, Index) + end; + + procedure SetPalette(Pal: array of TColor; Size: integer); + var + PalSize, i: integer; + Col: PFPCompactImgRGB8BitValue; + x, y: Cardinal; + Red, Green, Blue: Byte; + Pcol: PInteger; + DistanceSquared, SmallestDistanceSquared: integer; + R1, G1, B1: Byte; + begin + if Size <> -1 then PalSize := Size else PalSize := High(Pal) + 1; + for i := 0 to PalSize - 1 do + begin + GetMem(Col, SizeOf(TFPCompactImgRGB8BitValue)); + Col^.r := Byte(Pal[i]); + Col^.g := Byte(Pal[i] shr 8); + Col^.b := Byte(Pal[i] shr 16); + Palette.Add(Col); + end; + for y := 0 to $ff do + for x := 0 to $ff do + Mappings[y,x] := nil; + for y := 0 to Img.Height - 1 do + for x := 0 to Img.Width - 1 do + begin + Red := Byte(Img.Colors[x, y].red shr 8); + Green := Byte(Img.Colors[x, y].green shr 8); + Blue := Byte(Img.Colors[x, y].blue shr 8); + //Small reduction of color space + dec(Red, Red mod 3); + dec(Green, Green mod 3); + dec(Blue, Blue mod 3); + if (Mappings[Red, Green]) = nil then + begin + Mappings[Red, Green] := TList.Create; + Mappings[Red, Green].Count := 256; + end; + if (Mappings[Red, Green].Items[Blue] = nil) then + begin + GetMem(Pcol, SizeOf(integer)); + PCol^ := 0; + SmallestDistanceSquared := $1000000; + for i := 0 to Palette.Count - 1 do + begin + R1 := PFPCompactImgRGB8BitValue(Palette[i])^.r; + G1 := PFPCompactImgRGB8BitValue(Palette[i])^.g; + B1 := PFPCompactImgRGB8BitValue(Palette[i])^.b; + DistanceSquared := (Red - R1) * (Red - R1) + (Green - G1) * (Green - G1) + (Blue - B1) * (Blue - B1); + if DistanceSquared < SmallestDistanceSquared then + begin + PCol^ := i; + if (Red = R1) and (Green = G1) and (Blue = B1) then break; + SmallestDistanceSquared := DistanceSquared; + end + end; + Mappings[Red, Green].Items[Blue] := PCol; + end; + end; + end; + + procedure DeleteTree(var Node: TOctreeNode); + var i: integer; + begin + for i := Low(TReducibleNodes) to High(TReducibleNodes) do + if Node.Child[i] <> nil then + DeleteTree(Node.Child[i]); + FreeAndNil(Node); + end; + + var + i, j, Index: integer; + QArr: TRGBQuadArray256; + begin + PaletteHasAllColours := false; + Tree := nil; + LeafCount := 0; + for i := Low(ReducibleNodes) to High(ReducibleNodes) do + ReducibleNodes[i] := nil; + if (Img.Height > 0) and (Img.Width > 0) then + for j := 0 to Img.Height - 1 do + for i := 0 to Img.Width - 1 do + begin + AddColor(Tree, Byte(Img.Colors[i,j].red shr 8), Byte(Img.Colors[i,j].green shr 8), Byte(Img.Colors[i,j].blue shr 8), 0, ReducibleNodes); + while LeafCount > 256 do + ReduceTree(LeafCount, ReducibleNodes) + end; + Index := 0; + GetPaletteColors(Tree, QArr, Index); + for i := 0 to LeafCount - 1 do + CT[i] := (QArr[i].b shl 16) + (QArr[i].g shl 8) + QArr[i].r; + fColorTableSize := LeafCount; + for i := 0 to fColorTableSize - 1 do + fColorTable[i] := CT[i]; + LastColor := clNone; + SetPalette(fColorTable, LeafCount); + if Tree <> nil then DeleteTree(Tree); + end; + + procedure ClearMappings; + var i, j, k: integer; + begin + { wp: Avoids crash at FreeMem - may result in a memory leak !!!! + for j := 0 to $FF do + for i := 0 to $FF do + begin + if Assigned(Mappings[i, j]) then + begin + for k := 0 to $FF do + FreeMem(Mappings[i, j].Items[k], SizeOf(TColor)); + Mappings[i, j].Free; + end; + Mappings[i, j] := nil; + end; + } + end; + + procedure SetPixel(X, Y: Integer; Value: TColor); + var + Val: integer; + PCol: PInteger; + R, G, B: byte; + begin + if not ((Img.Width >= X) and (Img.Height >= Y) and (X > -1) and (Y > -1)) then exit; + Val := -1; + if LastColor = Value then + Val := LastColorIndex + else + begin + if PaletteHasAllColours then + begin + TFPCompactImgRGBA8BitValue(Value).a := 0; + for Val := 0 to fColorTableSize - 1 do + if fColorTable[Val] = Value then break; + end + else + begin + B := Byte(Value shr 16); + B := B - (B mod 3); + G := Byte(Value shr 8); + G := G - (G mod 3); + R := Byte(Value); + R := R - (R mod 3); + Val := -1; + if Mappings[R, G] <> nil then + begin + PCol := Mappings[R, G].Items[B]; + if PCol <> nil then Val := PCol^; + end; + end; + LastColor := Value; + LastColorIndex := Val; + end; + fPixels^[Y * Img.Width + X] := Val; + end; + + // find the color within the color table; returns 0..255, -1 if color not found + function FindColorIndex(c: TColor): integer; + var i: integer; + begin + i := 0; + result := -1; + while (i -1) then + begin + Packedbit := Packedbit or $01; // transparent color given (Packedbit or $01) + ColorIndex := n; //transparent color index + end; + end; + DelayTime := 0; + Terminator := 0; // allways 0 + end; + + SaveToStream(Stream); + + if (fPixelList <> nil) then FreeMem(fPixelList); + FreeMem(fPixels); + fPixels := nil; +end; +{$ENDREGION} + +initialization + ImageHandlers.RegisterImageWriter ('GIF Graphics', 'gif', TFPWriterGif); +end. diff --git a/components/lazbarcodes/examples/qrcodegenerator/main.lfm b/components/lazbarcodes/examples/qrcodegenerator/main.lfm new file mode 100644 index 000000000..c8277f9c8 --- /dev/null +++ b/components/lazbarcodes/examples/qrcodegenerator/main.lfm @@ -0,0 +1,166 @@ +object MainForm: TMainForm + Left = 403 + Height = 326 + Top = 256 + Width = 380 + Caption = 'QRCode Generator' + ClientHeight = 326 + ClientWidth = 380 + Constraints.MinWidth = 300 + OnActivate = FormActivate + OnDestroy = FormDestroy + LCLVersion = '2.3.0.0' + object btnCreateQR: TButton + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = edText + AnchorSideTop.Side = asrBottom + Left = 8 + Height = 25 + Top = 64 + Width = 108 + AutoSize = True + BorderSpacing.Left = 8 + BorderSpacing.Top = 16 + Caption = 'Create QR code' + OnClick = btnCreateQRClick + TabOrder = 0 + end + object edText: TEdit + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = Label1 + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + Left = 8 + Height = 23 + Top = 25 + Width = 364 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Left = 8 + BorderSpacing.Top = 2 + BorderSpacing.Right = 8 + OnChange = edTextChange + TabOrder = 1 + Text = 'https://www.lazarus-ide.org/' + end + object btnSaveAsGIF: TButton + AnchorSideLeft.Control = btnCreateQR + AnchorSideTop.Control = btnCreateQR + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = btnCreateQR + AnchorSideRight.Side = asrBottom + Left = 8 + Height = 25 + Top = 97 + Width = 108 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Top = 8 + Caption = 'Save as GIF' + OnClick = btnSaveAsGIFClick + TabOrder = 2 + Visible = False + end + object btnSaveAsBMP: TButton + AnchorSideLeft.Control = btnCreateQR + AnchorSideTop.Control = btnSaveAsGIF + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = btnCreateQR + AnchorSideRight.Side = asrBottom + Left = 8 + Height = 25 + Top = 130 + Width = 108 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Top = 8 + Caption = 'Save as BMP' + OnClick = btnSaveAsBMPClick + TabOrder = 3 + Visible = False + end + object StatusBar: TStatusBar + Left = 0 + Height = 23 + Top = 303 + Width = 380 + Panels = <> + end + object btnSaveAsPNG: TButton + AnchorSideLeft.Control = btnCreateQR + AnchorSideTop.Control = btnSaveAsBMP + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = btnCreateQR + AnchorSideRight.Side = asrBottom + Left = 8 + Height = 25 + Top = 163 + Width = 108 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Top = 8 + Caption = 'Save as PNG' + OnClick = btnSaveAsPNGClick + TabOrder = 5 + Visible = False + end + object btnSaveAsJPEG: TButton + AnchorSideLeft.Control = btnCreateQR + AnchorSideTop.Control = btnSaveAsPNG + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = btnCreateQR + AnchorSideRight.Side = asrBottom + Left = 8 + Height = 25 + Top = 196 + Width = 108 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Top = 8 + Caption = 'Save as JPEG' + OnClick = btnSaveAsPNGClick + TabOrder = 6 + Visible = False + end + object Label1: TLabel + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = Owner + Left = 8 + Height = 15 + Top = 8 + Width = 100 + BorderSpacing.Left = 8 + BorderSpacing.Top = 8 + Caption = 'Text to be encoded' + end + object btnSaveAsSVG: TButton + AnchorSideLeft.Control = btnCreateQR + AnchorSideTop.Control = btnSaveAsJPEG + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = btnCreateQR + AnchorSideRight.Side = asrBottom + Left = 8 + Height = 25 + Top = 229 + Width = 108 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Top = 8 + Caption = 'Save as SVG' + OnClick = btnSaveAsSVGClick + TabOrder = 7 + Visible = False + end + object btnSaveAsEPS: TButton + AnchorSideLeft.Control = btnCreateQR + AnchorSideTop.Control = btnSaveAsSVG + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = btnCreateQR + AnchorSideRight.Side = asrBottom + Left = 8 + Height = 25 + Top = 262 + Width = 108 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Top = 8 + Caption = 'Save as EPS' + OnClick = btnSaveAsEPSClick + TabOrder = 8 + Visible = False + end +end diff --git a/components/lazbarcodes/examples/qrcodegenerator/main.pas b/components/lazbarcodes/examples/qrcodegenerator/main.pas new file mode 100644 index 000000000..827fbc86c --- /dev/null +++ b/components/lazbarcodes/examples/qrcodegenerator/main.pas @@ -0,0 +1,170 @@ +unit main; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, + ComCtrls, LCLIntf, + fpimage, fpWriteGIF, + ubarcodes; + +type + TMyGifImage = class(TGifImage) + protected + class function GetWriterClass: TFPCustomImageWriterClass; override; + end; + + { TMainForm } + + TMainForm = class(TForm) + btnCreateQR: TButton; + btnSaveAsGIF: TButton; + btnSaveAsBMP: TButton; + btnSaveAsSVG: TButton; + btnSaveAsPNG: TButton; + btnSaveAsJPEG: TButton; + btnSaveAsEPS: TButton; + edText: TEdit; + Label1: TLabel; + StatusBar: TStatusBar; + procedure btnCreateQRClick(Sender: TObject); + procedure btnSaveAsEPSClick(Sender: TObject); + procedure btnSaveAsGIFClick(Sender: TObject); + procedure btnSaveAsBMPClick(Sender: TObject); + procedure btnSaveAsPNGClick(Sender: TObject); + procedure btnSaveAsSVGClick(Sender: TObject); + procedure edTextChange(Sender: TObject); + procedure FormActivate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + private + QRCode: TBarcodeQR; + procedure EnableButtons(Enable: Boolean); + procedure SaveQRCodeToImage(AFileName: String; AImageClass: TFPImageBitmapClass); + + public + + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.lfm} + +class function TMyGifImage.GetWriterClass: TFPCustomImageWriterClass; +begin + Result := TFPWriterGIF; +end; + +{ TMainForm } + +procedure TMainForm.btnCreateQRClick(Sender: TObject); +begin + QRCode.Free; + QRCode := TBarcodeQR.Create(self); + QRCode.Text := edText.Text; + QRCode.Top := btnCreateQR.Top; + QRCode.Left := btnCreateQR.Left + btnCreateQR.Width + 10; + QRCode.Width := 168; + QRCode.Height := 168; + QRCode.Generate; + QRCode.Parent := self; + + EnableButtons(true); + StatusBar.simpleText := 'QRCode generated.'; +end; + +procedure TMainForm.btnSaveAsEPSClick(Sender: TObject); +var + fn: String; +begin + if QRCode = nil then + exit; + fn := 'qrcode.eps'; + QRCode.SaveToEpsFile(fn); + StatusBar.SimpleText := 'QRCode saved to "' + fn + '".'; +end; + +procedure TMainForm.btnSaveAsGIFClick(Sender: TObject); +begin + SaveQRCodeToImage('qrcode', TMyGifImage); +end; + +procedure TMainForm.btnSaveAsBMPClick(Sender: TObject); +begin + SaveQRCodeToImage('qrcode', TBitmap); +end; + +procedure TMainForm.btnSaveAsPNGClick(Sender: TObject); +begin + SaveQRCodeToImage('qrcode', TPortableNetworkGraphic); +end; + +procedure TMainForm.btnSaveAsSVGClick(Sender: TObject); +var + fn: String; +begin + if QRCode = nil then + exit; + fn := 'qrcode.svg'; + QRCode.SaveToSvgFile(fn); + StatusBar.SimpleText := 'QRCode saved to "' + fn + '".'; +end; + +procedure TMainForm.edTextChange(Sender: TObject); +begin + EnableButtons(false); +end; + +procedure TMainForm.FormActivate(Sender: TObject); +begin + ClientHeight := btnSaveAsEPS.Top + btnSaveAsEPS.Height + StatusBar.Height + 16; +end; + +procedure TMainForm.EnableButtons(Enable: Boolean); +begin + btnSaveAsGIF.Visible := Enable; + btnSaveAsBMP.Visible := Enable; + btnSaveAsPNG.Visible := Enable; + btnSaveAsJPEG.Visible := Enable; + btnSaveAsSVG.Visible := Enable; + btnSaveAsEPS.Visible := Enable; + if not Enable then StatusBar.SimpleText := ''; +end; + +procedure TMainForm.FormDestroy(Sender: TObject); +begin + QRCode.Free; +end; + +procedure TMainForm.SaveQRCodeToImage(AFileName: String; AImageClass: TFPImageBitmapClass); +var + ext: String; +begin + if QRCode = nil then + exit; + + if AImageClass = TBitmap then + ext := '.bmp' + else if AImageClass = TMyGifImage then + ext := '.gif' + else if AImageClass = TPortableNetworkGraphic then + ext := '.png' + else if AImageClass = TJpegImage then + ext := '.jpg' + else + raise Exception.Create('Image format not supported.'); + + AFileName := ChangeFileExt(AFileName, ext); + QRCode.SaveToFile(AFileName, AImageClass); + + StatusBar.SimpleText := 'QRCode saved to "' + AFileName + '".'; +end; + +initialization + TPicture.RegisterFileFormat('.gif', 'gif', TMyGifImage); +end. + diff --git a/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.ico b/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.ico new file mode 100644 index 000000000..25c186a5b Binary files /dev/null and b/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.ico differ diff --git a/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.lpi b/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.lpi new file mode 100644 index 000000000..bd0f9fb4c --- /dev/null +++ b/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.lpi @@ -0,0 +1,87 @@ + + + + + + + + + + + + <Scaled Value="True"/> + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + <XPManifest> + <DpiAware Value="True"/> + </XPManifest> + <Icon Value="0"/> + </General> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <RequiredPackages Count="2"> + <Item1> + <PackageName Value="lazbarcodes"/> + </Item1> + <Item2> + <PackageName Value="LCL"/> + </Item2> + </RequiredPackages> + <Units Count="2"> + <Unit0> + <Filename Value="qrcodegen.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="QRCodeGen"/> + </Unit0> + <Unit1> + <Filename Value="main.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="MainForm"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + </Unit1> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="qrcodegen"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Debugging> + <DebugInfoType Value="dsDwarf3"/> + </Debugging> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.lpr b/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.lpr new file mode 100644 index 000000000..e7cf77259 --- /dev/null +++ b/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.lpr @@ -0,0 +1,25 @@ +program QRCodeGen; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX} + cthreads, + {$ENDIF} + {$IFDEF HASAMIGA} + athreads, + {$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, main + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource:=True; + Application.Scaled:=True; + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. + diff --git a/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.res b/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.res new file mode 100644 index 000000000..bcdc06674 Binary files /dev/null and b/components/lazbarcodes/examples/qrcodegenerator/qrcodegen.res differ