venerdì 24 novembre 2023
#
A common need when working with images is to convert arrays containing pixels from a type to another.
For example an uint[] to byte[] conversion is common, but very slow and memory intensive.
Here the fastest method I developed for real-time applications:
The code is unsafe (additional testing recommended) and may not work with future CLR Runtimes, but it's pretty readable and fast.
In few words the code is changing the type of the array by overwriting the CLR Method Table and then resizing the array in a similiar way. A new array (of the desired type) pointing to the original one is then returned. The original data is never copied and this makes this method very fast to execute.
Here how to use the function CastArray:
An array containing a 4K 32 bit image can be casted with a great speedup compared to an Array copy with standard solutions such as Buffer.BlockCopy.
The drawback is that now we have overwritten the original array size and if we use the original array, we should manually ensure that we are not reading over it's end.
The byte[] is actually a reference array and changing it will change the uint[] (and the opposite), but this can avoid further casts.
Note: As I already done in the electro-logic blog, new posts starting from this one are only in English to enable more people to read them, but you can still contact me or leave a comment in Italian if you prefer.
That's all for today
venerdì 24 marzo 2023
#
Una caratteristica di WPF e' l'indipendenza dalla risoluzione, in particolare le immagini bitmap vengono automaticamente ridimensionate a 96 DPI, valore di default storico di Windows.
Un'immagine 512x512 pixel a 72 DPI verra' quindi ridimensionata a 682.5 x 682.5 pixel circa. Questo succedera' indipendentemente dai DPI impostati nel sistema.
Utilizzando software professionali di editing di immagini come Adobe Photoshop, Gimp, etc.. e' possibile salvare i nostri capolavori con una risoluzione DPI specifica. Questa informazione (DpiX e DpiY) non modifica di fatto la dimensione in pixel dell'immagine, ma e' un attributo che viene salvato nei metadati del file e che puo' essere per esempio utile in fase di stampa... e in WPF.
Soddisfatti della nostra creazione, impostiamo quindi 96 DPI, certi che la nostra immagine non verra' mai e poi mai ridimensionata, ed aggiungiamo il seguente codice XAML alla nostra pagina:
La nostra immagine e' stata inserita in un controllo Viewbox a scopo dimostrativo per evitare ridimensionamenti dovuti a limiti di spazio della Window, selezionando il controllo Image notiamo pero' qualcosa di strano nel pannello delle proprieta' di Visual Studio.
L'immagine e' stata ridimensionata da WPF! Il nostro castello di carte e' cascato dalle fondamenta, manca circa 0.1 pixel per lato!
Questo puo' essere un problema trascurabile dal punto di vista grafico e prestazionale, ma cerchiamo ad ogni modo di capire cosa sta succendo.
Eseguendo il seguente codice C#
scopriamo una triste realta' che stravolgera' presto tutte le nostre certezze. L'immagine e' di fatto a 96.01 DPI!
Adobe e' impazzita? un Easter Eggs di Gimp? Un complotto di Visual Studio?
Vediamo quindi di fare chiarezza, il formato PNG salva la risoluzione in un chunk chiamato Physical pixel dimensions (pHYs) composto da tre campi:
- Pixels per unit, x axis (4 bytes, unsigned integer)
- Pixels per unit, y axis (4 bytes, unsigned integer)
- Unit specifier (1 byte)
dove lo
standard definisce come unita' valide solamente il metro o l'unita' indefinita. Il metro e' definito anche dal SI (Sistema Internazionale di unita' di misura), quindi a prima vista ha senso impiegarlo in uno standard al posto dei piedi o di altre unita' meno blasonate.
Utilizzando il metro stiamo quindi parlando di DPM (Dot per Meter). Un DPM equivale a 0.0254 DPI e sfortuna vuola che i campi prevedano solamente valori interi.
3779 DPM equivalgono a circa 95.99 DPI mentre 3780 a circa 96.01 DPI. Non esiste modo di specificare 96 DPI precisi, 0.01 DPI saranno sempre mancanti.
NB: Molti software arrotondano i valori di DPI visualizzati, prestare quindi sempre la massima attenzione.
Questo e' un bel dilemma per WPF, che di default usa proprio 96 DPI per le immagini, anche se a dire il vero non ho visto insurrezioni popolari in questi anni.
Un software utile per analizzare (e modificare) i metadati dei file PNG e'
TweakPNG, dove a prima vista i nostri sogni di perfezione DPI-eggianti tornano, per poi svanire all'amara realta' con un doppio click.
NB: Questo problema non affligge il formato TIFF dove l'unita' di risoluzione puo' essere impostata in pollici (inch).
Per motivi di orgoglio piu' che tecnici non vogliamo pero' convertire tutte le nostre icone al formato TIFF, cosa possiamo dunque fare?
La matematica non lascio molto scampo, ma la fantasia ha ancora qualche carta da giocare.
Per risolvere il problema possiamo procedere in due modi:
- cancellare il chunk pHYs (che e' opzionale) se creato dall'app di editing grafico.
- impostare ad "unspecified units" l'unita' di misura. I valori X ed Y verranno ignorati.
Entrambe queste soluzioni forzeranno a 96 DPI esatti la risoluzione dell'immagine in WPF ed eviteranno qualsiasi ridimensionamente.
Personalmente preferisco utilizzare utility come
PNGGauntlet che eliminano i chunk opzionali ed ottimizzano la dimensione del file (senza perdita di dati). Molto utile anche per evitare errori "App manifest references .. which is larger than the maximum image file size." durante il packaging di applicazioni UWP.
Avete mai notato che i 96 DPI esatti non esistono nei formati PNG e BMP? Dite la vostra nei commenti ed un saluto a tutti!
NB: Gli stessi concetti posso applicarsi ad altre tecnologie basate su XAML come ad esempio UWP
martedì 13 marzo 2018
#
Segnalo, grazie alla collaborazione di
Mauro Servienti un piccolo consiglio per chi posta su UGI
tramite l'interfaccia Web dell'amato/odiato SubText.
Nelle opzioni avanzate ricordatevi sempre di spuntare sia "
Syndicate on Main Feed" che "
Include in Aggregated Site" per apparire sul muro di UGI e nei feed RSS.
Di
default l'opzione "Include in Aggregated Site" non risulta infatti marcata ed i post non compaiono nell'aggregatore principale
http://blogs.ugidotnet.org.
Devo dire che avendo sempre utilizzando Windows Live Writer, ora sostituito da
Open Live Writer (
http://openlivewriter.org), non avevo mai avuto bisogno di fare questa operazione.
E adesso mi raccomando.. ricominciate ad imbrattare il muro!
domenica 11 marzo 2018
#
Ops.. è passato un bel pò di tempo dal mio ultimo post su UGI..
lo sapevo! E' successo ancora! :)
FPGA, IoT, Elettronica varia e diversi progetti mi hanno tenuto occupato quanto basta ed aggiornare due blog (per chi non lo sapesse, potete seguirmi anche su
http://electro-logic.blogspot.it) è abbastanza impegnativo. In realtà anche lato .NET non mi sono mai fermato (Xamarin, UWP, .NET Core, ...avrei tante tante cose da raccontare!)
Vediamo oggi come si è evoluta nel tempo la
Stack Traces in C#, ovvero, semplificando molto, l'elenco cronologico delle chiamate di funzione che ci hanno portato ad una determinata situazione.
Sono ormai passati i tempi (.NET 1.0) dove si aveva uno Stack Trace immediatamente comprensibile come
Con l'aggiunta di nuove funzionalità come i Generics, LINQ, i metodi Lambda, Async, etc.. oggi ci troviamo sempre più spesso ad una situazione di questo tipo (
se ci va bene):
Dove si riesce ancora a capire qualcosa, ma il tempo per avere una chiara visione di quello che sta succedendo aumenta inesorabilmente, se non altro per leggere delle informazioni spesso poco rilevanti e rifacenti a meccanismi interni che quasi mai hanno a che vedere coi problemi che come sviluppatori di
applicativi (e non di framework) cerchiamo di risolvere.
Il progetto
Ben.Demystifier (
https://github.com/benaadams/Ben.Demystifier) nasce per riportare la semplicità anche nello Stack Trace, interpretando pattern complessi ma estremamente ripetitivi, conseguenza delle nuove funzioni di .NET, e rendendoli immediatamente comprensibili, rimuovendo così la noiosa ridondanza dai nostri log.
Ecco come diventa lo Stack Trace precedente:
E questo il codice (esempio in .NET Core 2.0, C# 7.2) per generarlo
Come è possibile notare l'utilizzo è immediato, basta chiamare l'Extension Method
Demystify() sulle nostre eccezioni per avere una stringa.. demistificata. Veramente impressionante!
Ecco l'esempio dell'autore del progetto, ancora più incisivo, che potete trovare nella pagina principale del progetto:
Personalmente spero che venga in qualche modo direttamente integrato nel futuro .NET Core.
Per il momento utilizziamo la libreria tramite NuGet e diciamo addio a lunghe sessioni di
Stack Trace Understanding.
Ciao a tutti!
giovedì 16 gennaio 2014
#
Dopo tanti anni torno finalmente a scrivere sul mio vecchio blog (ultimamente mi sono dedicato maggiormente al mio blog di elettronica).
Volevo proporre un articolo che ho preparato nel 2011 ma mai pubblicato e che ho ritrovato in un vecchio hard-disk, come dice il detto… meglio tardi che mai. Buona lettura a tutti.
Se abbiamo un sito in hosting può tornare comodo in caso di upload di molti file di piccole dimensioni (ad esempio immagini multi-risoluzione Deep Zoom o HD View) comprimere il tutto, fare un unico upload e decomprimere lato server i nostri file.
Ho realizzato una piccola utility ASP.NET che ho utilizzato in occasione di un upload consistente e che voglio condividere con voi, l'ho utilizzata solamente una volta ma mi ha fatto veramente risparmiare molto tempo e banda di rete.
La pagina è veramente semplice, realizzarla e testarla richiede veramente poche tempo che… però può essere risparmiato scaricandola.
L'unica pagina dell'utility si presenta con le istruzioni molto semplici che non riporto nuovamente:
Come unica nota "/Upload" si riferisce alla cartella sul server web, posizionata nella cartella radice (ed in genere coincidente con il nome del dominio su hosting come Aruba) dove girerà la pagina che dovrà avere i corretti permessi impostati, in genere tramite le pagine di amministrazione dell'hosting.
Il codice dell'unico pulsante è veramente semplice, utilizza la libreria SharpZipLib per decomprimere il file ZIP caricato e non richiede molte spiegazioni, il file sarà decompresso in una cartella Upload/Data.
Buon upload!
Scarica il progetto
martedì 11 ottobre 2011
#
Quando si crea una proprietà di dipendenza non si gestisce direttamente il valore di default ma lo si specifica attraverso i metadati. Se la proprietà è un Reference Type il valore di default viene applicato a tutte le istanze del tipo e non alle singole istanze. Questo comportamento è particolarmente critico quando si lavora con delle collezioni.
Per capire meglio questo comportamento e le sue conseguenze consideriamo una semplice applicazione con una finestra contenente due istanze di un UserControl
L'UserControl (chiamato SampleUserControl) possiede una DependencyProperty con dati di tipo Reference come ad esempio il tipo Person.
Codice dell'UserControl (il codice XAML è omesso)
Il valore di default della DependencyProperty è impostato ad una nuova istanza di Person
Il controllo contiene una TextBox collegata tramite databinding (UpdateSourceTrigger=PropertyChanged) alla proprietà Name della proprietà Person del controllo stesso (TextBox.Text <=> UserControl.Person.Name).
Lanciando l'applicazione e iniziando a scrivere sulla TextBox del primo controllo cosa succederà?
Automaticamente anche la seconda TextBox verrà valorizzata con quanto scritto.
Cerchiamo di capire cosa succede:
- Alla creazione della prima istanza del controllo, dato che la proprietà non è valorizzata viene assegnato il valore di default, "una sorta di puntatore" ad un nuovo oggetto Person
- Alla creazione della seconda istanza del controllo la proprietà viene valorizzata con lo stesso puntatore. Entrambi i controlli puntano quindi allo stesso oggetto Person.
- Modificando la prima proprietà verrà modificato l'oggetto Person "puntato" anche dal secondo controllo. La modifica sarà quindi visibile anche dal secondo controllo.
Concludendo il comportamento è a prima vista differente dalle normali aspettative di un programmatore ma consente un notevole risparmio di memoria in controlli con molte proprietà non inizializzate.
Scarica l'applicazione di esempio
martedì 10 maggio 2011
#
Inviare la posta da una casella @miodominio.it in hosting presso Aruba tramite un client di posta comporta alcune difficoltà, riporta il sito di aruba la seguente nota:
Attenzione: E' possibile che alcuni fornitori di connessioni internet (ad esempio Tele2, Tre, ecc.. ) NON consentano l'invio dei messaggi da indirizzi legati a domini registrati con Aruba utilizzando smtp.nomedominio.xxx: in questi casi il parametro da inserire potrebbe ad esempio essere smtp.tele2.it e relativa autenticazione oppure smtp.tre.it. Ovviamente per ulteriori dettagli fare riferimento specifico al proprio provider di connessione.
Ma purtroppo non viene data direttamente una soluzione.
Per risolvere il problema alla radice è sufficiente utilizzare il protocollo SSL per inviare (ma è possibile anche per leggere) la propria posta da connessioni come l'adsl di Alice.
I dati da impostare nel proprio account sono:
Server posta in arrivo: pop3s.aruba.it
Server posta in uscita (SMTP): smtps.aruba.it
E' necessario impostare anche altre impostazioni:
Spuntare "Il server della posta in uscita (SMTP) richiede l'autenticazione" e selezionare "Utilizza le stesse impostazioni del server della posta in arrivo"
Nella scheda Impostazioni avanzate è necessario impostare la porta POP3 a 995, selezionare "Il server richiede una connessione crittografata (SSL)", impostare la porta SMTP a 465 e selezionare dal menù a discesa SSL come tipo di connessione crittografata.
Buona lettura della posta dal vostro client di posta preferito
venerdì 15 aprile 2011
#
Durante il test di alcune classi che elaborano file XML ho avuto la necessità di convertire diversi file XML in stringhe C# per evitare di avere dipendenze da file esterni.
Diciamo che convertire del testo in stringhe C# è un'operazione abbastanza noiosa perché comprende l'aggiunta di doppi apici per ogni riga e di slash aggiuntive, '\' diventa '\\' a meno di usare la keyword @.
Ho quindi creato una piccola utility che permette di svolgere in modo rapido questa operazione, se vi serve potete scaricarla assieme al sorgente per adattare meglio il programma alle vostre esigenze
Scarica "Paste Text as C# String"
martedì 25 gennaio 2011
#
Una novità passata perlopiù dietro le quinte di Windows 7 (HomePremium e versioni superiori) è l'inclusione dell'Assembly Microsoft.Ink (versione 6.1) nella GAC. Prima era necessario installare l'SDK dedicato e distribuire assieme alla propria applicazione le relative librerie.
Questo Assembly permette di utilizzare in applicazioni Windows Forms due nuovi controlli per supportare la scrittura ed il riconoscimento della calligrafia in maniera molto semplice: InkEdit ed InkPicture
Rimando all'articolo Add Support for Digital Ink to Your Windows Applications per una descrizione dell'uso di Microsoft.Ink e delle sue classi e controlli, anche se l'articolo fa riferimento alla versione dell'SDK e non a quella shippata con Windows 7, l'utilizzo è analogo.
Purtroppo l'Assembly non include controlli per WPF e quindi se si vuole utilizzare il riconoscimento automatico della scrittura in WPF la strada più semplice rimane installare l'SDK dedicato allo sviluppo per Tablet PC.
Per poter iniziare a utilizzare questo Assembly ho trovato molto utile l'Addin Muse per Visual Studio 2010 che permette di visualizzare e filtrare gli Assembly nella GAC. Ricordo che per Visual Studio normalmente non mostra tutti gli Assembly della GAC ma solamente un sottoinsieme contenuto in C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0
Una volta aggiunto il riferimento alla versione 6.1.0 di Microsoft.Ink sarà sufficiente scegliere Choose Items.. tramite tasto destro sulla barra degli strumenti e tramite Sfoglia cercare C:\Windows\assembly\GAC_32\Microsoft.Ink\6.1.0.0__31bf3856ad364e35\Microsoft.Ink.dll (per la versione a 32 bit) ovvero il percorso fisico dell'Assembly.
Troveremo ora nella nostra casella degli strumenti i due nuovi controlli e trascinando il controllo InkEdit sul nostro forms avremo un RichEditControl evoluto che riconoscerà in automatico evenutuali scritte a mano (bisogna avere una tavoletta grafica o una schermo touch per poter scrivere sul controllo) e le convertirà senza scrivere una riga di codice in testo dopo qualche istante.
sabato 22 gennaio 2011
#
Il DataBinding in WPF è sicuramente un elemento essenziale ed un primo cittadino ma supponiamo di avere una collezione generica come ObservableCollection<T>, dove T è una nostra classe, per esempio la classica classe Person contenente le proprietà Nome, Cognome e Descrizione: come facciamo a visualizzare tramite XAML nella nostra finestra la descrizione di Mario Rossi?
Diagramma della classe Person
Questo scenario non è direttamente supportato in quanto la sintassi di Binding permette di specificare proprietà, sottoproprietà ed indicizzatori ma non selezionare oggetti in collezioni dato il valore di alcune loro proprietà.
NON è possibile per esempio scrivere, supponendo come contesto dei dati la collezione (DataContext=Collezione), codice XAML di questo tipo
<TextBlock Text="{Binding Path='Name=[Mario],SurName=[Rossi],Path=[Description]'}" />
per selezionare la descrizione dell'oggetto della collezione che ha come valore della proprietà Name "Mario" e come valore della proprietà SurName il valore "Rossi".
Vediamo come risolvere questo problema:
Per semplicità ci limiteremo a supportare la selezione della proprietà ToString che nel caso della classe Person restituirà esattamente "Name Surname".
Potremmo cioè poter scrivere
<TextBlock Text="{Binding Path='Mario Rossi.Description'}" />
ma nulla vieta di estendere questo esempio per supportare una sintassi "XPath o Linq Like".
Ricapitolando il DataBinding viene effettuato su Proprietà, dobbiamo perciò far credere al motore di DataBinding di WPF che la nostra collezione espone una proprietà "Nome Cognome" che restituisce il nostro oggetto Person selezionato.
Questo problema è risolvibile estendendo tramite l'ereditarietà la classe ObservableCollection<T> e implementando l'interfaccia ICustomTypeDescriptor che per l'appunto permette di fornire informazioni dinamiche di tipo personalizzato come ad esempio le proprietà che espone una classe.
Se la nostra classe non espone questa interfaccia il motore di DataBinding utilizzerà la Reflection per capire quali proprietà sono disponibili.
NOTA BENE: La Reflection è relativamente lenta e sebbene possano essere utilizzati internamente dei meccanismi di caching, per velocizzare questa operazione sono nati progetti come HyperDescriptor.
L'interfaccia ICustomTypeDescriptor non è tra le più snelle del Framework ed infatti contiene 12 metodi ed è in genere preferibile derivare dalla classe CustomTypeDescriptor ed effettuare solamente gli override di nostro interesse ma purtroppo la nostra ereditarietà è già stata "bruciata" con ObservableCollection<T> e la nostra scelta deve ricadere obbligatoriamente sull'interfaccia.
Anche se a prima vista questa interfaccia potrebbe intimorire a noi interessa veramente solo il metodo GetProperties, possiamo quindi limitarci nell'implementazione degli altri metodi a ritornare i valori di default.
NOTA BENE: Per migliorare le prestazioni possiamo implementare un sistema di caching
Il metodo GetProperties dell'interfaccia ICustomTypeDescriptor serve proprio a ritornare al Framework una collezione di proprietà, basterà quindi eseguire un ciclo su tutti gli elementi della collezione e ritornare per ogni elemento una proprietà chiamata con il metodo ToString dell'oggetto e che contenga come valore l'oggetto stesso.
Esempio di implementazione di GetProperties
Le proprietà sono rappresentate dalla classe astratta PropertyDescriptor, creiamo dunque una classe generica GenericPropertyDescriptor<T> derivante da tale classe con un semplice costruttore che accetta in ingresso un oggetto T e che tramite gli override ad GetValue ed SetValue restituisce ed imposta il valore dell'oggetto. Per impostare il nome della proprietà la classe dovrà chiamare il costruttore della classe base passandogli in questo caso la stringa ritornata dal metodo ToString dell'oggetto.
NOTA BENE: In uno scenario più flessibile probabilmente vorremmo poter passare una funzione agente sull'oggetto per determinare il nome della proprietà
ATTENZIONE: Il nome della proprietà non dovrebbe contenere caratteri come il punto che sono interpretati come sottoproprietà dal motore di DataBinding di WPF
La classe astratta PropertyDescriptor definisce anche altre proprietà e metodi astratti che possiamo però semplicemente implementare con valori di default visto che l'importante è che la proprietà restituisca, imposti e si chiami in un certo modo. Rimando all'analisi del codice allegato per i dettagli.
Diagramma della classe GenericPropertyDescriptor<T>
Il gioco è quasi ultimato, creiamo ora per provare le nostre classi una Window con impostata essa stessa come DataContext e con esposta tramite una proprietà la nostra ObservablePropertyCollection<Person> popolata con alcune persone di esempio.
Nelllo XAML corrispettivo aggiungiamo uno StackPanel che visualizzi tramite TextBlock le varie proprietà dell'elemento Mario Rossi
E il gioco è fatto. Lo XAML risulta immediatamente intuitivo anche se a Design-Time purtroppo non visualizzerà alcun risultato.
Nel codice allegato è presente inoltre un pulsante che dimostra come la soluzione proposta, a differenza di soluzioni a base di Converter, supporta la notifica delle singole proprietà dell'elemento selezionato.
Codice Allegato
Missione completata