I would like to ask if is correct way of using it when I want to save file in memory from writer and convert it to BufferedImage or should i write it to outputstream and convert it to BufferedImage?
Link to documentation https://sksamuel.github.io/scrimage/io/
My code looks like this:
def getImage(url: URL, width: Int, height: Int): BufferedImage = {
val writer = new PngWriter()
val image = ImmutableImage.loader().fromStream(url.openStream()).fit(width, height)
image.replaceTransparencyInPlace(Color.white)
ImageIO.read(image.forWriter(writer).stream())
}
If you want to read a URL and return a java BufferedImage then you should a) close the stream once complete and b) there is no need to reparse the image, just return the awt image that Scrimage uses under the hood.
If you are using Scala 2.13 then you can use Using to handle resources.
For example:
def getImage(url: URL, width: Int, height: Int): BufferedImage = {
val writer = new PngWriter()
Using(url.openStream()) { stream =>
val image = ImmutableImage.loader().fromStream(stream).fit(width, height)
image.replaceTransparencyInPlace(Color.white).awt()
}
}
Related
Current project:
ASP.NET 4.5.2
MVC 5
I am trying to leverage the TinyPNG API, and if I just pipe the image over to it, it works great. However, since the majority of users will be on a mobile device, and these produce images at a far higher resolution than what is needed, I am hoping to reduce the resolution of these files prior to them being piped over to TinyPNG. It is my hope that these resized images will be considerably smaller than the originals, allowing me to conduct a faster round trip.
My code:
public static async Task<byte[]> TinyPng(Stream input, int aspect) {
using(Stream output = new MemoryStream())
using(var png = new TinyPngClient("kxR5d49mYik37CISWkJlC6YQjFMcUZI0")) {
ResizeImage(input, output, aspect, aspect); // Problem area
var result = await png.Compress(output);
using(var reader = new BinaryReader(await (await png.Download(result)).GetImageStreamData())) {
return reader.ReadBytes(result.Output.Size);
}
}
}
public static void ResizeImage(Stream input, Stream output, int newWidth, int maxHeight) {
using(var srcImage = Image.FromStream(input)) {
var newHeight = srcImage.Height * newWidth / srcImage.Width;
if(newHeight > maxHeight) {
newWidth = srcImage.Width * maxHeight / srcImage.Height;
newHeight = maxHeight;
}
using(var newImage = new Bitmap(newWidth, newHeight))
using(var gr = Graphics.FromImage(newImage)) {
gr.SmoothingMode = SmoothingMode.AntiAlias;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(srcImage, new Rectangle(0, 0, newWidth, newHeight));
newImage.Save(output, ImageFormat.Jpeg);
}
}
}
So the ResizeArea is supposed to accept a stream and output a stream, meaning that the TinyPNG .Compress() should work just as well with the output as it would with the original input. Unfortunately, only the .Compress(input) works -- with .Compress(output) TinyPNG throws back an error:
400 - Bad Request. InputMissing, File is empty
I know TinyPNG has its own resizing routines, but I want to do this before the image is sent out over the wire to TinyPNG so that file size (and therefore transmission time) is reduced as much as possible prior to the actual TinyPNG compression.
…Aaaaand I just solved my problem by using another tool entirely.
I found ImageProcessor. Documentation is a royal b**ch to get at because it only comes in a Windows *.chm help file (it’s not online… cue one epic Whisky. Tango. Foxtrot.), but after looking at a few examples it did solve my issue quite nicely:
public static async Task<byte[]> TinyPng(Stream input, int aspect) {
using(var output = new MemoryStream())
using(var png = new TinyPngClient("kxR5d49mYik37CISWkJlC6YQjFMcUZI0")) {
using(var imageFactory = new ImageFactory()) {
imageFactory.Load(input).Resize(new Size(aspect, 0)).Save(output);
}
var result = await png.Compress(output);
using(var reader = new BinaryReader(await (await png.Download(result)).GetImageStreamData())) {
return reader.ReadBytes(result.Output.Size);
}
}
}
and everything is working fine now. Uploads are much faster now as I am not piping a full-sized image straight through to TinyPNG, and since I am storing both final-“full”-sized images as well as thumbnails straight into the database, I am now not piping the whole bloody image twice.
Posted so that other wheel-reinventing chuckleheads like me will actually have something to go on.
For personal needs, for the Xamarin.Forms.Map control, I need to create a CustomPin extension. UWP part (PCL project)
I create a MapIcon like it:
nativeMap.MapElements.Add(new MapIcon()
{
Title = pin.Name,
Image = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Pin/customicon.png")),
Location = new Geopoint(new BasicGeoposition() { Latitude = pin.Position.Latitude, Longitude = pin.Position.Longitude }),
NormalizedAnchorPoint = new Windows.Foundation.Point(0.5, 1.0)
});
However, by this way, I can't set the Image's size.
I then want to use an Image from my PCL part, resize it and convert it into a IRandomAccessStreamReference. To realize it, I need to convert my Image into a stream, but I can't find the way to make it works ><
Example of the function needed:
private IRandomAccessStreamReference ImageToIRandomAccessStreamReference(Image image)
{
//Here I can set the size of my Image
//I convert it into a stream
IRandomAccessStreamReference irasr = RandomAccessStreamReference.CreateFromStream(/* img? */);
//irasr is then created from img
//I return the IRandomAccessStreamReference needed by the MapIcon element
return irasr;
}
Note: The Image paramter img is a Xamarin.Forms.Image
So first, is it possible? If yes, then thank for any help which could help me.. I already search about how to resize the MapIcon and it's not possible directly from the class [MapIcon].(https://msdn.microsoft.com/library/windows/apps/windows.ui.xaml.controls.maps.mapicon.aspx)
Thank for help !
You are right. We can't resize the MapIcon directly as it doesn't provide such properties or methods. MapIcon's size is mostly controlled by the size of image which is set by MapIcon.Image property. And we can set this image's size without using Xamarin.Forms.Image.
To set this image's size, we can take advantage of BitmapDecoder class, BitmapEncoder class and BitmapTransform class like following:
private async System.Threading.Tasks.Task<RandomAccessStreamReference> ResizeImage(StorageFile imageFile, uint scaledWidth, uint scaledHeight)
{
using (IRandomAccessStream fileStream = await imageFile.OpenAsync(FileAccessMode.Read))
{
var decoder = await BitmapDecoder.CreateAsync(fileStream);
//create a RandomAccessStream as output stream
var memStream = new InMemoryRandomAccessStream();
//creates a new BitmapEncoder and initializes it using data from an existing BitmapDecoder
BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(memStream, decoder);
//resize the image
encoder.BitmapTransform.ScaledWidth = scaledWidth;
encoder.BitmapTransform.ScaledHeight = scaledHeight;
//commits and flushes all of the image data
await encoder.FlushAsync();
//return the output stream as RandomAccessStreamReference
return RandomAccessStreamReference.CreateFromStream(memStream);
}
}
And then we can use this method to create a resized image stream reference first and then set it as MapIcon's Image like:
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Pin/customicon.png"));
var imageReference = await ResizeImage(file, 64, 64);
nativeMap.MapElements.Add(new MapIcon()
{
Title = pin.Name,
Image = imageReference,
Location = new Geopoint(new BasicGeoposition() { Latitude = pin.Position.Latitude, Longitude = pin.Position.Longitude }),
NormalizedAnchorPoint = new Windows.Foundation.Point(0.5, 1.0)
});
I have an asp.net web site that serves sample MP3 files to client Flash Players (SWF).
These files are downloadable by tons of download tools.
Although only registered members can access the high quality mp3 samples, my client wants to prevent these low quality MP3 files to be downloaded by download tools.
So I thought about this solution:
Convert these MP3 files to bytearrays on server side (ASP.NET)
Do some bitwise XOR operations (Simple encryption)
Write this array to aspx' responsestream
Modify Flash (.fla) to request to this new file/page/aspx
Do some bitwise XOR operations on Flash and convert it to the original MP3 as byte array. (Simple decryption)
Play the MP3
I was able to succeed till step 6. I cannot convert this byte array to a Sound object that Flash can play. I did a bit by bit comparison of the resulting array on the flash and the source array on ASP.NET. They are equal.
I'm open to completely different approaches. But I cannot use Flash Media Server. I need to be using Flash as3 and ASP.NET.
Also very important! The .mp3 must be downloaded/decrypted and played asynchronously (which I coud not succeed in doing)
I agree with Peter Elliot that authentication probably is the easiest way to restrict access to the files. However, if you still need to explore the route of encrypting the files, I thought I'd expand a bit on Alex Vlad's answer.
What you need to do in order to be able to stream the audio file, decrypt it on the fly, and play it asynchronously is to use the URLStream class (docs) in conjunction with the Sound class (docs) and keeping a buffer of the partially downloaded content.
Some pseudocode to illustrate:
class AsyncEncryptedSoundPlayer extends Sound {
var buffer:ByteArray;
var stream:URLStream;
var currSoundPosition:uint = 0;
public function AsyncEncryptedSoundPlayer(url:String) {
buffer = new ByteArray();
stream = new URLStream();
stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
stream.load(new URLRequest(url));
addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleDataRequested);
}
function onProgress(e:ProgressEvent):void {
var tmpData:ByteArray;
stream.readBytes(tmpData, buffer.length, stream.bytesAvailable - buffer.length);
var decryptedData:ByteArray = decryptData(tmpData); // Decrypt loaded data
buffer.writeBytes(decryptedData, buffer.length, decryptedData.length); // Add decrypted data to buffer
}
function onSampleDataRequested(e:ProgressEvent):void {
// Feed samples from the buffer to the Sound instance
// You may have to pause the audio to increase the buffer it the download speed isn't high enough
event.data.writeBytes(buffer, currSoundPosition, 2048);
currSoundPosition += 2048;
}
function decryptedData(data:ByteArray):void {
// Returns decrypted data
}
}
This is obviously a very rough outline of a class, but I hope it will point you in the right direction.
#walkietokyo, thanks a lot for pointing me to the right direction. I succeeded in doing what I wanted. The keyword here was the loadCompressedDataFromByteArray function.
After tens of trial and errors I found out that loadCompressedDataFromByteArray was working in a differential manner.
It appends anything that it converts to the end of the sound object data.
Another issue: sound object doesn't continue playing the parts appended by loadCompressedDataFromByteArray after its play function is called.
So I implemented a sort of double buffering. Where I use 2 sound objects interchangeably.
My final (test) version is listed below. With the encryption (obfuscation) method I used (a simple XOR) no download manager or grabber or sniffer that I tested was able to play the Mp3s.
Flash (Client) side:
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.OutputProgressEvent;
import flash.events.ProgressEvent;
import flash.net.URLRequest;
import flash.net.URLStream;
import flash.utils.ByteArray;
import flashx.textLayout.formats.Float;
var buffer:ByteArray;
var stream:URLStream;
var bufferReadPosition:uint = 0;
var bufferWritePosition:uint = 0;
var url:String = "http://www.blablabla.com/MusicServer.aspx?" + (new Date());
buffer = new ByteArray();
stream = new URLStream();
stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
stream.load(new URLRequest(url));
var s1:Sound = new Sound();
var s2:Sound = new Sound();
var channel1:SoundChannel;
var channel2:SoundChannel;
var pausePosition:int = 0;
var aSoundIsPlaying:Boolean = false;
var lastLoadedS1:Boolean = false;
var lastS1Length:int = 0;
var lastS2Length:int = 0;
function onProgress(e:ProgressEvent):void {
var tmpData:ByteArray = new ByteArray();
stream.readBytes(tmpData, 0, stream.bytesAvailable);
var decryptedData:ByteArray = decryptData(tmpData); // Decrypt loaded data
buffer.position = bufferWritePosition;
buffer.writeBytes(decryptedData, 0, decryptedData.length); // Add decrypted data to buffer
bufferWritePosition += decryptedData.length;
if(lastLoadedS1)
{
buffer.position = lastS2Length;
s2.loadCompressedDataFromByteArray(buffer, buffer.length - lastS2Length);
lastS2Length = buffer.length;
}
else
{
buffer.position = lastS1Length;
s1.loadCompressedDataFromByteArray(buffer, buffer.length - lastS1Length);
lastS1Length = buffer.length;
}
if(!aSoundIsPlaying)
{
DecidePlay();
}
}
function channel1Completed(e:Event):void
{
DecidePlay();
}
function channel2Completed(e:Event):void
{
DecidePlay();
}
function DecidePlay():void
{
aSoundIsPlaying = false;
if(lastLoadedS1)
{
channel1.stop();
if(s2.length - s1.length > 10000)
{
//At least a 10 second buffer
channel2 = s2.play(s1.length);
channel2.addEventListener(Event.SOUND_COMPLETE, channel2Completed);
lastLoadedS1 = false;
aSoundIsPlaying = true;
}
}
else
{
if(channel2 != null)
{
channel2.stop();
}
if(s1.length - s2.length > 10000)
{
//At least a 10 second buffer
channel1 = s1.play(s2.length);
channel1.addEventListener(Event.SOUND_COMPLETE, channel1Completed);
lastLoadedS1 = true;
aSoundIsPlaying = true;
}
}
}
function decryptData(data:ByteArray):ByteArray {
for(var i:int = 0;i<data.length;i++)
{
//Here put in your bitwise decryption code
}
return data;
}
ASP.NET server side (MusicServer.aspx):
protected void Page_Load(object sender, EventArgs e)
{
CopyStream(Mp3ToStream(Server.MapPath("blabla.mp3")), Response.OutputStream);
this.Response.AddHeader("Content-Disposition", "blabla.mp3");
this.Response.ContentType = "audio/mpeg";
this.Response.End();
}
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[32768];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
for (int i = 0; i < read; i++)
{
//Here put in your bitwise encryption code
}
output.Write(buffer, 0, read);
}
}
public Stream Mp3ToStream(string filePath)
{
using (FileStream fileStream = File.OpenRead(filePath))
{
MemoryStream memStream = new MemoryStream();
memStream.SetLength(fileStream.Length);
fileStream.Read(memStream.GetBuffer(), 0, (int)fileStream.Length);
return memStream;
}
}
what might be simpler than encrypting the data coming back from your service is instead authenticating requests so that only your swf can request the files.
You can accomplish this in the same way that say, the Amazon APIs work: build a request that includes a number of parameters, including a timestamp. hash all of these arguments together in an HMAC (HMAC-SHA256 is available in the as3crypto library) along with a private key embedded in your swf. Your server end authenticates this request, ensuring that the hash is valid and that it is close enough to the timestamp. Any requests with a bad hash, or using a request with a timestamp too far in the past (replay attack) are denied.
This is certainly not perfect security. Any sufficiently motivated user could disassemble your swf and pull out your auth key, or grab the mp3 from their browser cache. But then again, any mechanism you are going to use will have those issues. This removes the overhead of having to encrypt and decrypt all of your files, instead moving the work over to the request generation phase.
Flash Sound supports only streaming mp3 playing that is you can play only mp3 by direct link. But you can send swf file with embeded mp3 withing it and this swf can be encrypted in the same way as you encrypt mp3.
as3 code for embedding and using mp3:
public class Sounds
{
[Embed(source="/../assets/sounds/sound1.mp3")]
private static const sound1:Class;
}
after loading this swf by the Loader you can access to the sound in this way:
var domain:ApplicationDomain = ApplicationDomain.currentDomain; // <-- ApplicationDomain where you load sounds.swf
var soundClass:Class = domain.getDefinition("Sounds_sound1");
var sound:Sound = new soundClass();
sound.play();
Be sure, that you do at least one of the follows:
give different names for sound class (sound1)
give different name for holder class (Sounds)
or load sound.swf into different application domains
to prevent class names overlapping.
Unfortunate this approach doesn't allow you to streaming play sound, you have to load whole swf, decrypt it and only after that you will be able to play sound.
please have a look here:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/SampleDataEvent.html
and here:
http://help.adobe.com/en_US/as3/dev/WSE523B839-C626-4983-B9C0-07CF1A087ED7.html
I'm currently working on a data-check for images. I need to request the Size (width & height) and the resolution of the image. Files over 70MB throw an "out of memory" exception on GDI Problem. Is there an alternative way to get the file-information? The same error on parse it through FromStream...
Using myfile = Image.FromFile(filePath)
...
End Using
You can use the following code to get image properties (it loads metadata only):
using (var fs = new FileStream(#"C:\Users\Dmitry\Pictures\blue-earth-wallpaper.jpg", FileMode.Open, FileAccess.Read)) {
var decoder = BitmapDecoder.Create(fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
var size = decoder.Frames[0].PixelWidth;
var height = decoder.Frames[0].PixelHeight;
var dpiX = decoder.Frames[0].DpiX;
var dpiY = decoder.Frames[0].DpiY;
}
I found this link http://www.fastgraph.com/help/image_file_header_formats.html that tells where in the file you can find the type and its dimensions. I guess, if you use something like this below to seek and get the first few bytes and close once you are done, shouldnt be using much resources
Untested code below...
// This really needs to be a member-level variable;
private static readonly object fsLock = new object();
// Instantiate this in a static constructor or initialize() method
private static FileStream fs = new FileStream("myFile.txt", FileMode.Open);
public string ReadFile(int fileOffset) {
byte[] buffer = new byte[bufferSize];
int arrayOffset = 0;
lock (fsLock) {
fs.Seek(fileOffset, SeekOrigin.Begin);
int numBytesRead = fs.Read(bytes, arrayOffset , bufferSize);
// Typically used if you're in a loop, reading blocks at a time
arrayOffset += numBytesRead;
}
// Do what you want to the byte array and close
}
I use the following to upload a file to Flex:
private var filer:FileReference;
protected function button1_clickHandler(event:MouseEvent):void
{
var fd:String = "Files (*)";
var fe:String = "*";
var ff:FileFilter = new FileFilter(fd, fe);
filer = new FileReference();
filer.addEventListener(Event.SELECT, onFileSelect);
filer.browse(new Array(ff));
filer.addEventListener(Event.COMPLETE,
function (e:Event):void {
e.currentTarget.data.toString();
}
);
}
private function onFileSelect(e:Event):void {
filer.load();
}
And my file looks like this:
Here is the original file: http://sesija.com/up/1.txt
I need to read the uploaded file and parse it. The problem is that in my e.currentTarget.data.toString(); I get only '1' and not the rest of the String.
Any idea on how to successfully read this entire txt file?
The data property is a ByteArray. Instead of using the toString method (which apparently treats NULL byte as end of string), use specific read methods of the ByteArray class like readByte, readInt etc.
var array:Array = [];
var ba:ByteArray = e.currentTarget.data as ByteArray;
while(ba.bytesAvailable != 0){
array.push(ba.readByte());
}
trace(array.join(", "));
You might want to read Working with byte arrays