FMX datasnap send a bitmap as a JPG - firemonkey

in Firemonkey, im currently sending a tbitmap as a stream to a datasnap server - but need to reduce the size and possibly lower the quality prior to sending. I would like to do on-the-fly conversion from the TBitmap to a TJpgImage format as its saved into the TMemoryStream.
if possible - The solution should include a variable for setting the compressionQuality
var
ms:TMemmoryStream;
begin
ms:=TMemmoryStream.create;
bmpSend.SaveToStream(msSendPic); //somehow convert bmp to jpg on-the-fly
sz := msSendPic.size;
msSendPic.Position :=0;
svr.setPic(s, sz, msSendPic);
end;
Thanks in advance

Use TCodecManager and a TBitmapSurface (unit FMX.surfaces)
var Surf:TBitmapSurFace := TBitmapSurface.Create;
try
Surf.Assign(bmpSend);
// use the codec to save Surface to stream
TBitmapCodecManager.SaveToStream(ms,Surf,'.jpg');
finally
Surf.Free;
end;

Related

Firemonkey assign to bitmap after effect

With FMX, I'm using a standard FMX effect on a TImage and trying to get the bitmap after the effect has been executed - say for example a TColorKeyAlphaEffect. I used the structure pallet to associate the TColorKeyAlphaEffect with the TImage.
When I assign the bitmap of the Timage to another bitmap (or save to file), I only get the original picture prior to the effect - not what is displayed.
Did not find a way to extract the bitmap from the effect itself nor post-effect from the TImage.
Any code for assigning or copying from the TImage to the bmpFinal would do the job and appreciated.
bmpFinal.CopyFromBitmap(TImage.picture.graphic, rect(0,0,iWidth, iHeight), 0, 0 );
TIA
As quick response you can use Image1.MakeScreenshot.SaveToFile('yourfilename.ext');
as long if (2 Timage involved)
Between 2 images it's easy Image2.Bitmap:=Image1.MakeScreenshot;
and you can create effect as runtime i.e
uses FMX.Filter.Effects;
procedure TForm1.Button1Click(Sender: TObject);
var aBitmap : Tbitmap;
begin
if OpenDialog1.Execute then
begin
Image1.Bitmap.LoadFromFile(Opendialog1.FileName);
ABitmap:=Tbitmap.CreateFromFile(Opendialog1.FileName);
try
with TmonochromeEffect.Create(nil) do
try
ProcessEffect(nil,aBitmap, 0);
finally
Free;
end;
image2.Bitmap:=ABitmap;
finally
aBitmap.Free;
end;
end;
end;

TimageList does not contain a member named GetBitmap

I want to load pictures from an ImageList to a TImage (mobile application, fmx). The TImage is part of my customstyle Listbox (LBItem.StylesData['myimage']). The standard approach would be ImageList.GetBitmap(). However the GetBitmap method gives me an error: 'TimageList does not contain a member named GetBitmap'. Any explanation or alternatives? Thanks in advance!
procedure TForm3.Button1Click(Sender: TObject);
var
i : Integer;
LBItem : TListBoxItem;
Bitmap : TBitMap;
begin
ListBox1.BeginUpdate;
ListBox1.Items.Clear;
Bitmap := TBitMap.Create;
try
for i := 0 to 3 do begin
LBItem := TListBoxItem.Create(nil);
LBItem.Parent := ListBox1;
LBItem.StyleLookup := 'mystyle';
LBItem.StylesData['mylabel'] := 'Some text...';
//Bitmap.LoadFromFile('D:\Koala.jpg');
ImageList1.GetBitmap(i, Bitmap);
LBItem.StylesData['myimage']:= Bitmap;
end;
finally
ListBox1.EndUpdate;
end;
end;
Assuming you have an TImage with name Image1, a TImageList with name ImageList1 and at least one entry in the list with image for scale 1.0 called Image1Hover, then you can use the following example to load a "hover picture" in the OnEnter event of Image1:
procedure TForm1.Image1MouseEnter(Sender: TObject);
var
Item: TCustomBitmapItem;
Size: TSize;
begin
ImageList1.BitmapItemByName('Image1Hover', Item, Size);
Image1.Bitmap := Item.MultiResBitmap.Bitmaps[1.0];
end;
This answer is translate from fire-monkey.ru
Use ImageList1.Bitmap(Size, Index);. The size is in physical pixels, i.e. we consider the scale independently (this method knows nothing about the scale of the canvas). This function selects the most appropriate size of the image that is available.
So, your code should look something like this:
LBItem.StylesData['myimage'] := ImageList1.Bitmap(
TSizeF.Create(myImageWidth * Canvas.Scale, myImageHeight * Canvas.Scale),
i);
// Not sure of the correctness of this assignment to 'myimage'
Note 1 All the bitmaps obtained in the 'ImageList1.Bitmap` are stored in the imagelist cache. So don't release them.
Note 2 ListBox has internal mechanism to interact with ImageList. Try to use icon: TImage style item and LBItem.ImageIndex property, without load bitmaps.
In FMX you don't need any additional coding for that, just use TGlyph instead of TImage if you want to display images directly form ImageList.
example :
Glyph1.ImageIndex := i;

Go base64 image decode

Im currently getting a base64 image data url from a canvas something like this (not the dataurl im getting just to show how the string looks like)
data:image/png;base64,iVkhdfjdAjdfirtn=
I need to decode that image to check the width and the height of the image
dataurl := strings.Replace(req.PostFormValue("dataurl"), "data:image/png;base64,", "", 1)
reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(dataurl))
c, _, err := image.DecodeConfig(reader)
if err != nil {
log.Fatal(err)
}
log.Println(c.Width)
But Im getting an error while trying to decode the config
Unknown image format
So yeah the way Im making the dataurl must be wrong but cant figure what to do. I also tried passing the full dataurl (with data:image...) still no success
What you have is a Data URI scheme, info on how to decode it and more on this is in this question and answer:
Illegal base64 data at input byte 4 when using base64.StdEncoding.DecodeString(str)
But note that image.Decodeconfig() will only decode image formats that are registered prior to calling this function, so you need the image format handlers to be registered in advance. This can be done with imports like
import _ "image/png"
More on this is in the package doc of image. Or if you know the exact format (e.g. in your example it's PNG), you can directly use png.DecodeConfig().
So it doesn't work for you because your actual encoded image is of PNG format, but you didn't register the the PNG format handler and so image.DecodeConfig() won't use the PNG handler (and so it will not be able to decode it => "Unknown image format").
Also note that replacing the prefix that is not part of the Base64 encoded image is a poor solution to get rid of it. Instead simply slice the input string:
input := "data:image/png;base64,iVkhdfjdAjdfirtn="
b64data := input[strings.IndexByte(input, ',')+1:]
Slicing a string will not even copy the string in memory, it will just create a new (two-word) string header.

How to get dimensions of an Image which is in Clipboard?

I want to know width and height of Image while it is in Clipboard, because if dimensions are too small then message like "Image is too small" should appear.
How to get width and height?
Unless you are prepared to manually parse the various image formats that you want to support, you can have the VCL simply load the image for you (just make sure suitable TGraphic classes have been registered, such as TGIFImage, TJPEGImage, TPNGImage, etc), and then you can ask the image for its dimensions, eg:
uses
Graphics, Clipbrd, Jpeg, PngImage, ...;
procedure TForm1.BitBtn1Click(Sender: TObject);
var
p: TPicture;
begin
p := TPicture.Create;
try
try
p.Assign(Clipboard);
// use p.Graphic, p.Graphic.Width, p.Graphic.Height as needed...
except
// unable to access Clipboard, or Clipboard
// does not contain a supported image type
end;
finally
p.Free;
end;
end;
If this is about bitmap I think you may try this.
procedure TForm1.BitBtn1Click(Sender: TObject);
var b:TBitmap;
begin
if Clipboard.HasFormat(CF_BITMAP) then begin
b:=TBitmap.Create;
try
b.Assign(Clipboard);
ShowMessage(IntToStr(b.Width)+','+IntToStr(b.Height));
finally
b.Free;
end;
end;
end;
you can instead of showmessage put If-statement and do what ever you want.

how to read bitmap and meta-data from a png stream/file efficiently?

I need to decode both bitmap and meta data from PNG input stream using [PNGJ] (http://code.google.com/p/pngj/) library. The problem is that decoding meta data will advance the stream and then I cannot use
bitmap = BitmapFactory.decodeStream().
Creating Bitmap on my own is OK but if I need to, say, scale bitmap with interpolation I'd rather use BitmapFactory. To use it I have to create a copy of InputStream every time I have to use PNGJ for getting meta data and BitmapFactory for getting a bitmap. It will be nice to return meta data AND Bitmap from a single PNGJ call (at least for most common ARGB_8888 format).
In a nutshell, I have to copy the stream to be used by Java libraries which looks like a waste. Returning a bitmap will be one solution.
// register an auxilary chunk name
PngChunk.factoryRegister(ThumbNailProvider.chunkID, chunkPROP.class);
// reader for the stream
PngReader pngr = new PngReader(inStream, "debug label PNG reader");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PngWriter pngw = new PngWriter(outputStream, pngr.imgInfo);
// copy pre-data chunks
pngw.copyChunksFirst(pngr, ChunkCopyBehaviour.COPY_ALL_SAFE);
// copy image data
for (int row = 0; row < pngr.imgInfo.rows; row++) {
ImageLine l1 = pngr.readRow(row);
pngw.writeRow(l1, row);
}
// copy after-data chunks
pngw.copyChunksLast(pngr, ChunkCopyBehaviour.COPY_ALL);
pngr.end(); // close inStream but not its copy
pngw.end(); // close out stream
// save a copy of the stream for Java Libraries;
data.inputStream = new ByteArrayInputStream(outputStream.toByteArray());
// read the chunk
ChunksList chunkList = pngr.getChunksList();
PngChunk chunk = chunkList.getById1(L2ThumbNailProvider.chunkID);
if (chunk != null) {
...
}
This is the problem of having two independent streams consumers, say Class1.parse(inputStream), Class2.decode(inputStream) and we want them to consume the same single stream; it has no simple elegant solutions (eg), if we have no control on how the consumers eat the stream.
Simple solutions, but not very elegant -and probably impractical- are: close and reopen the stream (unfeasible if we are reading from a network stream), buffer the full stream content in memory, or to a temporary file.
In your concrete case, the alternatives I can think of are:
1) Let PNGJ consume and decode the data and create the Bitmap yourself, filling the pixels it with setPixels(). This, among other inconveniences, would require you to do the proper color conversions.
2) Use PngReader as a InputFilterStream, so that it only parses the metadata and pass the full stream to the consumer. Currently, this is not possible, withouth tweaking on the PNGJ code. I will give it a look, and if a implement this feature I'll post it here.

Resources