If the image is png format, the function GetScaledDimensions of SKCodec always return the original size?
for eg:
public SKSizeI GetScaledDimensions(float desiredScale) and desiredScale = 0.5f;
but the return is still the original size.
So how we scale down a png image ?
It might be the case that the PNG decoder can't actually do downscaling during the load operation. So, to scale an image you will have to load it and then resize it:
var bitmap = SKBitmap.Decode("file.png");
var scaled = bitmap.Resize(new SKImageInfo(newWidth, newHeight), SKBitmapResizeMethod.Lanczos3);
There is more info on the docs website:
https://developer.xamarin.com/api/member/SkiaSharp.SKBitmap.Resize/p/SkiaSharp.SKImageInfo/SkiaSharp.SKBitmapResizeMethod/
@mattleibow are this resized images can be saved to disk also?
@AwsomeCode Yes:
SKBitmap bitmap = ...;
SKImage image = SKImage.FromBitmap(bitmap);
SKData png = image.Encode(SKImageEncodeFormat.Png, 100);
// save
using (var filestream = File.OpenWrite("image.png")) {
png.SaveTo(filestream);
}
@mattleibow
Thanks for your reply.
But what I want is loading a scale down image into memory.
If I do as you said
"var bitmap = SKBitmap.Decode("file.png");
var scaled = bitmap.Resize(new SKImageInfo(newWidth, newHeight), SKBitmapResizeMethod.Lanczos3);"
and if the png file is very big, it may cause "OutOfMemoryException "
Could you please have a look at this article https://developer.xamarin.com/recipes/android/resources/general/load_large_bitmaps_efficiently/
I just want to put this here for the record. This is how you can scale some codecs (.ico, .jpg, .raw, .webp) when loading:
int desiredWidth = 200;
// create the codec
SKCodec codec = SKCodec.Create(stream);
SKImageInfo info = codec.Info;
// get the scale that is nearest to what we want (eg: jpg returned 512)
SKSizeI supportedScale = codec.GetScaledDimensions((float)desiredWidth / info.Width);
// decode the bitmap at the nearest size
SKImageInfo nearest = new SKImageInfo(supportedScale.Width, supportedScale.Height);
SKBitmap bmp = SKBitmap.Decode(codec, nearest);
// now scale that to the size that we want
float realScale = (float)info.Height / info.Width;
SKImageInfo desired = new SKImageInfo(desiredWidth, (int)(realScale * desiredWidth);
bmp = bmp.Resize(desired), SKBitmapResizeMethod.Lanczos3);
@JessieZM I am still looking to see what can be done, but I am not too hopeful since it has to be supported by the underlying image and the codec.
@JessieZM I am going to as on the forums: https://groups.google.com/forum/#!forum/skia-discuss
But I don't think this is possible for .png as not even Android does it. My code:
var input = Assets.Open("baboon.png");
var reqHeight = 100;
var reqWidth = 200;
BitmapFactory.Options options = new BitmapFactory.Options();
options.InJustDecodeBounds = true;
BitmapFactory.DecodeStream(input, null, options);
int height = options.OutHeight;
int width = options.OutWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth)
{
int halfHeight = height / 2;
int halfWidth = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth)
{
inSampleSize *= 2;
}
}
options.InSampleSize = inSampleSize;
options.InJustDecodeBounds = false;
var bitmap = BitmapFactory.DecodeStream(input, null, options);
Android docs: https://developer.android.com/topic/performance/graphics/load-bitmap.html
@JessieZM I created a post here: https://groups.google.com/forum/#!topic/skia-discuss/_sfXByrGL08
Watch that and see what the experts say. I will of course bind any members that you need.
From that thread, it looks like Android's Codec has an option to limit the sampling on decoding for PNG:
SkCodec supports scaled jpeg and webp decodes because the native decoding libraries (libjpeg-turbo, libwebp) support downscaling. Ex: the jpeg library can perform a faster decode when downscaling because it will drop some of the DCT coefficients. There's no similar option for png.
However, SkAndroidCodec (wraps SkCodec) allows for decoding any format with a fSampleSize specified in AndroidOptions. fSampleSize=N instructs the codec to sample every Nth pixel as it decodes. This should do what you want. https://github.com/google/skia/blob/master/include/codec/SkAndroidCodec.h
Is this supposed to still work in the current version? With
var bitmap = SKBitmap.Decode(path, new SKImageInfo(256, 256));
all I get is a null for bitmap. Without the extra info, it works all right. Images are JPGs in the 3,000-4,000 pixel wide range.
@deakjahn This is due to the limitations of some of the codec decoding processes not being able to resize the image at the same time. @mattleibow is going to investigate change the decode to apply the resize after the image is decoded.
Regards
Ohh, that was some time ago. I switched to only using the dimensions returned by codec.GetScaledDimensions() and those always work. Not necessarily the same 256×256 or whatever I originally wanted, but close enough for me.
The point of the process is to have a cheaper decoding process (less time, less memory), so doing a full decode and then resize is not something Matthew should do, I guess. That goes against the benefit we can get from the process.
Seems to be resolved now
Most helpful comment
@AwsomeCode Yes: