source: 2013/30/MiskaK/MW2(My Warfare 2)/Paranneltu Jypeli/Graphics/Image.cs @ 4507

Revision 4507, 41.6 KB checked in by anlakane, 6 years ago (diff)

Talletus.

Line 
1using Microsoft.Xna.Framework.Graphics;
2using Microsoft.Xna.Framework;
3using System;
4using System.IO;
5using System.Collections.Generic;
6using System.ComponentModel;
7using System.Diagnostics;
8
9using XnaRectangle = Microsoft.Xna.Framework.Rectangle;
10using XnaV2 = Microsoft.Xna.Framework.Vector2;
11using XnaColor = Microsoft.Xna.Framework.Color;
12using JyColor = Jypeli.Color;
13
14#if !XBOX
15using System.Net;
16using System.Threading;
17#endif
18
19namespace Jypeli
20{
21    /// <summary>
22    /// Kuva.
23    /// </summary>
24    public class Image
25    {
26        private Image parentImage;
27        private XnaRectangle parentRectangle;
28
29        private string assetName;
30        private Texture2D xnaTexture;
31
32        private event Action InitDimensions;
33        private event Action InitTexture;
34
35        int _width = -1;
36        int _height = -1;
37
38        public Texture2D XNATexture
39        {
40            get { DoInitTexture(); return xnaTexture; }
41        }
42
43        public Color this[int row, int col]
44        {
45            get
46            {
47                DoInitTexture();
48
49                Color[] buffer = new Color[1];
50                XnaRectangle rect = new XnaRectangle( col, row, 1, 1 );
51                xnaTexture.GetData<Color>( 0, rect, buffer, 0, 1 );
52                return buffer[0];
53            }
54            set
55            {
56                DoInitTexture();
57                InvalidateAsset();
58
59                if ( row < 0 || row >= xnaTexture.Height ) throw new IndexOutOfRangeException( "row" );
60                if ( col < 0 || col >= XNATexture.Width ) throw new IndexOutOfRangeException( "col" );
61
62                Color[] buffer = new Color[1] { value };
63                XnaRectangle rect = new XnaRectangle( col, row, 1, 1 );
64
65                xnaTexture.SetData<Color>( 0, rect, buffer, 0, 1 );
66                UpdateTexture();
67            }
68        }
69
70
71        /// <summary>
72        /// Kuvan pikselit Color-taulukkona
73        /// </summary>
74        /// <param name="ox">siirtymä x-suunnassa vasemmasta ylänurkasta</param>
75        /// <param name="oy">siirtymä y-suunnassa vasemmasta ylänurkasta</param>
76        /// <param name="w">alueen leveys joka kopioidaan</param>
77        /// <param name="h">lueen korkaus joka kopioidaan</param>
78        /// <returns>pikselit Color-taulukkona</returns>
79        public Color[,] GetData( int ox = 0, int oy = 0, int w = int.MaxValue, int h = int.MaxValue )
80        {
81            int ny = Height;
82            if ( h < ny ) ny = h;
83            if ( Height < ny + oy ) ny = Height - oy;
84            int nx = Width;
85            if ( w < nx ) nx = w;
86            if ( Width < nx + ox ) nx = Width - ox;
87            if ( nx <= 0 || ny <= 0 ) return new Color[0, 0];
88
89            DoInitTexture();
90            Color[,] bmp = new Color[ny, nx];
91
92            XnaRectangle rect = new XnaRectangle( ox, oy, nx, ny );
93            Color[] buffer = new Color[ny * nx];
94            xnaTexture.GetData<Color>( 0, rect, buffer, 0, buffer.Length );
95            int i = 0;
96            for ( int iy = 0; iy < ny; iy++ )
97                for ( int ix = 0; ix < nx; ix++ )
98                    bmp[iy, ix] = buffer[i++];
99            return bmp;
100        }
101
102
103        /// <summary>
104        /// Asettaa kuvan pikselit Color-taulukosta
105        /// </summary>
106        /// <param name="bmp">taulukko josta pikseleitä otetaan</param>
107        /// <param name="ox">siirtymä x-suunnassa vasemmasta ylänurkasta</param>
108        /// <param name="oy">siirtymä y-suunnassa vasemmasta ylänurkasta</param>
109        /// <param name="w">alueen leveys johon kopioidaan</param>
110        /// <param name="h">lueen korkaus johon kopioidaan</param>
111        /// <returns>pikselit Color-taulukkona</returns>
112        public void SetData( Color[,] bmp, int ox = 0, int oy = 0, int w = int.MaxValue, int h = int.MaxValue )
113        {
114            DoInitTexture();
115            InvalidateAsset();
116            int ny = bmp.GetLength( 0 );
117            int nx = bmp.GetLength( 1 );
118            if ( ny > Height ) ny = Height;
119            if ( nx > Width ) nx = Height;
120            if ( ny > h ) ny = h;
121            if ( nx > w ) nx = w;
122            if ( Height < ny + oy ) ny = Height - oy;
123            if ( Width < nx + ox ) nx = Width - ox;
124            if ( nx <= 0 || ny <= 0 ) return;
125
126            XnaRectangle rect = new XnaRectangle( ox, oy, nx, ny );
127            Color[] buffer = new Color[ny * nx];
128            int i = 0;
129            for ( int iy = 0; iy < ny; iy++ )
130                for ( int ix = 0; ix < nx; ix++ )
131                    buffer[i++] = bmp[iy, ix];
132
133            xnaTexture.SetData<Color>( 0, rect, buffer, 0, buffer.Length );
134            UpdateTexture();
135        }
136
137
138        /// <summary>
139        /// Palalutetaan kuvan pikselit ARGB-uint[,] -taulukkona
140        /// </summary>
141        /// <param name="ox">siirtymä x-suunnassa vasemmasta ylänurkasta</param>
142        /// <param name="oy">siirtymä y-suunnassa vasemmasta ylänurkasta</param>
143        /// <param name="w">alueen leveys joka kopioidaan</param>
144        /// <param name="h">lueen korkaus joka kopioidaan</param>
145        /// <returns>Kuvan pikselit ARGB-taulukkona</returns>
146        public uint[,] GetDataUInt( int ox = 0, int oy = 0, int w = int.MaxValue, int h = int.MaxValue )
147        {
148            int ny = Height;
149            if ( h < ny ) ny = h;
150            if ( Height < ny + oy ) ny = Height - oy;
151            int nx = Width;
152            if ( w < nx ) nx = w;
153            if ( Width < nx + ox ) nx = Width - ox;
154            if ( nx <= 0 || ny <= 0 ) return new uint[0, 0];
155
156            DoInitTexture();
157            uint[,] bmp = new uint[ny, nx];
158
159            XnaRectangle rect = new XnaRectangle( ox, oy, nx, ny );
160            Color[] buffer = new Color[ny * nx];
161            xnaTexture.GetData<Color>( 0, rect, buffer, 0, buffer.Length );
162            int i = 0;
163            for ( int iy = 0; iy < ny; iy++ )
164                for ( int ix = 0; ix < nx; ix++ )
165                    bmp[iy, ix] = buffer[i++].ToUInt();
166            return bmp;
167        }
168
169
170        /// <summary>
171        /// Palalutetaan kuvan pikselit ARGB-uint[][] -taulukkona
172        /// </summary>
173        /// <param name="ox">siirtymä x-suunnassa vasemmasta ylänurkasta</param>
174        /// <param name="oy">siirtymä y-suunnassa vasemmasta ylänurkasta</param>
175        /// <param name="w">alueen leveys joka kopioidaan</param>
176        /// <param name="h">lueen korkaus joka kopioidaan</param>
177        /// <returns>Kuvan pikselit ARGB-taulukkona</returns>
178        public uint[][] GetDataUIntAA( int ox = 0, int oy = 0, int w = int.MaxValue, int h = int.MaxValue )
179        {
180            int ny = Height;
181            if ( h < ny ) ny = h;
182            if ( Height < ny + oy ) ny = Height - oy;
183            int nx = Width;
184            if ( w < nx ) nx = w;
185            if ( Width < nx + ox ) nx = Width - ox;
186            if ( nx <= 0 || ny <= 0 ) return new uint[0][];
187
188            DoInitTexture();
189            uint[][] bmp = new uint[ny][];
190
191            XnaRectangle rect = new XnaRectangle( ox, oy, nx, ny );
192            Color[] buffer = new Color[ny * nx];
193            xnaTexture.GetData<Color>( 0, rect, buffer, 0, buffer.Length );
194            int i = 0;
195            for ( int iy = 0; iy < ny; iy++ )
196            {
197                uint[] row = new uint[nx];
198                bmp[iy] = row;
199                for ( int ix = 0; ix < nx; ix++ )
200                    row[ix] = buffer[i++].ToUInt();
201            }
202            return bmp;
203        }
204
205
206        /// <summary>
207        /// Asetetaan kuvan pikselit ARGB-uint taulukosta
208        /// </summary>
209        /// <param name="bmp">taulukko josta pikselit otetaan</param>
210        /// <param name="ox">siirtymä x-suunnassa vasemmasta ylänurkasta</param>
211        /// <param name="oy">siirtymä y-suunnassa vasemmasta ylänurkasta</param>
212        /// <param name="w">alueen leveys johon kopioidaan</param>
213        /// <param name="h">alueen korkeus johon kopioidaan</param>
214        public void SetData( uint[,] bmp, int ox = 0, int oy = 0, int w = int.MaxValue, int h = int.MaxValue )
215        {
216            DoInitTexture();
217            InvalidateAsset();
218            int ny = bmp.GetLength( 0 );
219            int nx = bmp.GetLength( 1 );
220            if ( ny > Height ) ny = Height;
221            if ( nx > Width ) nx = Width;
222            if ( ny > h ) ny = h;
223            if ( nx > w ) nx = w;
224            if ( Height < ny + oy ) ny = Height - oy;
225            if ( Width < nx + ox ) nx = Width - ox;
226
227            if ( nx <= 0 || ny <= 0 ) return;
228
229            XnaRectangle rect = new XnaRectangle( ox, oy, nx, ny );
230            Color[] buffer = new Color[ny * nx];
231            int i = 0;
232            for ( int iy = 0; iy < ny; iy++ )
233                for ( int ix = 0; ix < nx; ix++ )
234                    buffer[i++] = Jypeli.Color.UIntToColor( bmp[iy, ix] );
235            // foreach (int c in bmp) buffer[i++] = Jypeli.Color.IntToColor(c);
236
237            xnaTexture.SetData<Color>( 0, rect, buffer, 0, buffer.Length );
238            UpdateTexture();
239        }
240
241
242        /// <summary>
243        /// Asetetaan kuvan pikselit ARGB-uint taulukosta
244        /// </summary>
245        /// <param name="bmp">taulukko josta pikselit otetaan</param>
246        /// <param name="ox">siirtymä x-suunnassa vasemmasta ylänurkasta</param>
247        /// <param name="oy">siirtymä y-suunnassa vasemmasta ylänurkasta</param>
248        /// <param name="w">alueen leveys johon kopioidaan</param>
249        /// <param name="h">alueen korkeus johon kopioidaan</param>
250        public void SetData( uint[][] bmp, int ox = 0, int oy = 0, int w = int.MaxValue, int h = int.MaxValue )
251        {
252            DoInitTexture();
253            InvalidateAsset();
254            int ny = bmp.Length;
255            int nx = bmp[0].Length;
256            if ( ny > Height ) ny = Height;
257            if ( nx > Width ) nx = Width;
258            if ( ny > h ) ny = h;
259            if ( nx > w ) nx = w;
260            if ( nx <= 0 || ny <= 0 ) return;
261
262            XnaRectangle rect = new XnaRectangle( ox, oy, nx, ny );
263            Color[] buffer = new Color[ny * nx];
264            int i = 0;
265            for ( int iy = 0; iy < ny; iy++ )
266                for ( int ix = 0; ix < nx; ix++ )
267                    buffer[i++] = Jypeli.Color.UIntToColor( bmp[iy][ix] );
268            // foreach (int c in bmp) buffer[i++] = Jypeli.Color.IntToColor(c);
269
270            xnaTexture.SetData<Color>( 0, rect, buffer, 0, buffer.Length );
271            UpdateTexture();
272        }
273
274
275        /// <summary>
276        /// Leveys pikseleinä.
277        /// </summary>
278        public int Width
279        {
280            get { DoInitDimensions(); return _width; }
281        }
282
283        /// <summary>
284        /// Korkeus pikseleinä.
285        /// </summary>
286        public int Height
287        {
288            get { DoInitDimensions(); return _height; }
289        }
290
291        /// <summary>
292        /// Nimi.
293        /// </summary>
294        public string Name
295        {
296            get { DoInitTexture(); return xnaTexture.Name; }
297        }
298
299        internal Image( int width, int height )
300        {
301            this._width = width;
302            this._height = height;
303            this.InitTexture += CreateNewTexture;
304        }
305
306        internal Image( string assetName )
307        {
308            this.assetName = assetName;
309            this.InitDimensions += LoadContentTexture;
310        }
311
312        [EditorBrowsable( EditorBrowsableState.Never )]
313        public Image( Microsoft.Xna.Framework.Graphics.Texture2D texture )
314        {
315            this.xnaTexture = texture;
316            this._width = texture.Width;
317            this._height = texture.Height;
318        }
319
320        /// <summary>
321        /// Luo uuden kuvan.
322        /// </summary>
323        /// <param name="width">Kuvan leveys</param>
324        /// <param name="height">Kuvan korkeus</param>
325        /// <param name="backColor">Kuvan taustaväri</param>
326        public Image( int width, int height, Color backColor )
327        {
328            assetName = null;
329            this._width = width;
330            this._height = height;
331            this.InitTexture += CreateNewTexture;
332            this.InitTexture += delegate { this.Fill( backColor ); };
333        }
334
335        /// <summary>
336        /// Luo uuden kuvan.
337        /// </summary>
338        /// <param name="width">Kuvan leveys</param>
339        /// <param name="height">Kuvan korkeus</param>
340        /// <param name="backColor">Kuvan taustaväri</param>
341        public Image( double width, double height, Color backColor )
342            : this( (int)Math.Round( width ), (int)Math.Round( height ), backColor )
343        {
344        }
345
346        private void DoInitDimensions()
347        {
348            if ( _width >= 0 && _height >= 0 )
349                return;
350
351            if ( InitDimensions != null )
352                InitDimensions();
353            else
354                throw new InvalidOperationException( "Cannot initialize dimensions for image!" );
355        }
356
357        private void DoInitTexture()
358        {
359            DoInitDimensions();
360
361            if ( xnaTexture != null )
362                return;
363
364            if ( InitTexture != null )
365                InitTexture();
366            else
367                throw new InvalidOperationException( "Cannot initialize texture for image!" );
368        }
369
370        private void LoadContentTexture()
371        {
372            // Some duct tape around the fact that in XNA,
373            // content can not be loaded before LoadContent().
374            Debug.Assert( assetName != null );
375            xnaTexture = Game.Instance.Content.Load<Texture2D>( assetName );
376            _width = xnaTexture.Width;
377            _height = xnaTexture.Height;
378        }
379
380        private void CreateNewTexture()
381        {
382            this.xnaTexture = new Texture2D( Game.GraphicsDevice, Width, Height );
383        }
384
385        public Image Clone()
386        {
387            Image copy;
388
389            if ( assetName != null )
390            {
391                copy = new Image( assetName );
392            }
393            else
394            {
395                copy = new Image( this.Width, this.Height );
396                copy.InitTexture += delegate { CopyData( copy, this ); };
397            }
398
399            return copy;
400        }
401
402        private static void CopyData( Image dest, Image src )
403        {
404            src.DoInitTexture();
405
406            int w = src.Width;
407            int h = src.Height;
408            XnaRectangle rect = new XnaRectangle( 0, 0, w, 1 );
409            Color[] scanline = new Color[w];
410
411            for ( rect.Y = 0; rect.Y < h; rect.Y++ )
412            {
413                src.xnaTexture.GetData<Color>( 0, rect, scanline, 0, w );
414                dest.xnaTexture.SetData<Color>( 0, rect, scanline, 0, w );
415            }
416        }
417
418        private static void CopyData( Texture2D dest, Texture2D src )
419        {
420            int w = src.Width;
421            int h = src.Height;
422            XnaRectangle rect = new XnaRectangle( 0, 0, w, 1 );
423            Color[] scanline = new Color[w];
424
425            for ( rect.Y = 0; rect.Y < h; rect.Y++ )
426            {
427                src.GetData<Color>( 0, rect, scanline, 0, w );
428                dest.SetData<Color>( 0, rect, scanline, 0, w );
429            }
430        }
431
432        private static void CopyData( Image dest, Image src, XnaRectangle destRect, XnaRectangle srcRect )
433        {
434            src.DoInitTexture();
435
436            int w = srcRect.Width;
437            int h = srcRect.Height;
438            Color[] buffer = new Color[w * h];
439            //XnaRectangle srcScan = new XnaRectangle( 0, 0, w, 1 );
440            //Color[] scanline = new Color[w];
441
442            //for ( rect.Y = 0; rect.Y < h; rect.Y++ )
443            //{
444            //    src.xnaTexture.GetData<Color>( 0, rect, scanline, 0, w );
445            //    dest.xnaTexture.SetData<Color>( 0, rect, scanline, 0, w );
446            //}
447
448            src.xnaTexture.GetData<Color>( 0, srcRect, buffer, 0, w * h );
449            dest.xnaTexture.SetData<Color>( 0, destRect, buffer, 0, w * h );
450        }
451
452        /// <summary>
453        /// Suorittaa annetun pikselioperaation koko kuvalle.
454        /// </summary>
455        /// <param name="operation">Aliohjelma, joka ottaa värin ja palauttaa värin</param>
456        public void ApplyPixelOperation( Converter<Color, Color> operation )
457        {
458            Converter<XnaColor, XnaColor> newOp = delegate( XnaColor c )
459            {
460                return operation( new Color( c ) ).AsXnaColor();
461            };
462
463            ApplyPixelOperation( newOp );
464        }
465
466        /// <summary>
467        /// Suorittaa annetun pikselioperaation koko kuvalle.
468        /// </summary>
469        /// <param name="operation">Aliohjelma, joka ottaa värin ja palauttaa värin</param>
470        internal void ApplyPixelOperation( Converter<XnaColor, XnaColor> operation )
471        {
472            DoInitTexture();
473            InvalidateAsset();
474
475            XnaRectangle scanRect = new XnaRectangle( 0, 0, xnaTexture.Width, 1 );
476            XnaColor[] scanline = new XnaColor[xnaTexture.Width];
477
478            for ( scanRect.Y = 0; scanRect.Y < xnaTexture.Height; scanRect.Y++ )
479            {
480                xnaTexture.GetData<XnaColor>( 0, scanRect, scanline, 0, xnaTexture.Width );
481
482                for ( int j = 0; j < xnaTexture.Width; j++ )
483                {
484                    scanline[j] = operation( scanline[j] );
485                }
486
487                xnaTexture.SetData<XnaColor>( 0, scanRect, scanline, 0, xnaTexture.Width );
488            }
489
490            UpdateTexture();
491        }
492
493        /// <summary>
494        /// Tekee uuden lokaalin instanssin kuvan tekstuurista ja poistaa
495        /// viitteen assettiin josta kuva on luotu.
496        /// Kutsu tätä metodia aina kun kuvan dataa muutetaan.
497        /// </summary>
498        private void InvalidateAsset()
499        {
500            if ( assetName == null )
501                return;
502
503            Texture2D oldTex = xnaTexture;
504            CreateNewTexture();
505            CopyData( xnaTexture, oldTex );
506            assetName = null;
507        }
508
509        private void UpdateTexture()
510        {
511            Game.DoNextUpdate( DoUpdateTexture );
512        }
513
514        private void DoUpdateTexture()
515        {
516            if ( parentImage != null )
517            {
518                XnaRectangle srcRect = new XnaRectangle( 0, 0, Width, Height );
519                CopyData( parentImage, this, parentRectangle, srcRect );
520                parentImage.UpdateTexture();
521            }
522        }
523
524        #region static methods
525
526        /// <summary>
527        /// Lataa kuvan tiedostosta. Kuvan ei tarvitse olla lisättynä
528        /// Content-projektiin.
529        /// </summary>
530        /// <param name="path">Tiedoston polku.</param>
531#if !XBOX
532        public static Image FromFile( string path )
533        {
534            StreamReader sr = new StreamReader( path );
535            Image img = new Image( Texture2D.FromStream( Game.GraphicsDevice, sr.BaseStream ) );
536            return img;
537        }
538
539#endif
540
541        /// <summary>
542        /// Lataa kuvan tiedostosta. Kuvan ei tarvitse olla lisättynä
543        /// Content-projektiin.
544        /// </summary>
545        /// <param name="path">Tiedosto.</param>
546        public static Image FromFile( StorageFile file )
547        {
548            return FromStream( file.Stream );
549        }
550
551        /// <summary>
552        /// Lataa kuvan tiedostovirrasta.
553        /// </summary>
554        /// <param name="stream"></param>
555        /// <returns></returns>
556        public static Image FromStream( Stream stream )
557        {
558            return new Image( Texture2D.FromStream( Game.GraphicsDevice, stream ) );
559        }
560
561#if WINDOWS
562        /// <summary>
563        /// Lataa kuvan Internetistä.
564        /// </summary>
565        /// <param name="url">Kuvan URL-osoite</param>
566        /// <returns>Kuva</returns>
567        public static Image FromURL( string url )
568        {
569            HttpWebRequest request = (HttpWebRequest)WebRequest.Create( url );
570            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
571            Stream resStream = response.GetResponseStream();
572
573            MemoryStream memStream = new MemoryStream();
574            resStream.CopyTo( memStream );
575
576            Image img = new Image( Texture2D.FromStream( Game.GraphicsDevice, memStream ) );
577            return img;
578        }
579#endif
580
581        /// <summary>
582        /// Luo tähtitaivaskuvan.
583        /// </summary>
584        /// <param name="width">Tekstuurin leveys.</param>
585        /// <param name="height">Tekstuurin korkeus.</param>
586        /// <param name="stars">Tähtien määrä.</param>
587        /// <returns>Tekstuuri.</returns>
588        public static Image CreateStarSky( int width, int height, int stars )
589        {
590            XnaColor[] textureColors = new XnaColor[width * height];
591
592            // Black background
593            int i = 0;
594            for ( int ver = 0; ver < height; ver++ )
595            {
596                for ( int hor = 0; hor < width; hor++ )
597                {
598                    textureColors[i++] = XnaColor.Black;
599                }
600            }
601
602            // Random stars
603            for ( int j = 0; j < stars; j++ )
604            {
605                int star = RandomGen.NextInt( 0, width * height );
606                int size = RandomGen.NextInt( 1, 5 );
607
608                for ( int k = 0; k < size / 2; k++ )
609                {
610                    XnaColor starcolor = RandomGen.NextColor( Jypeli.Color.White, new Color( 192, 192, 192, 255 ) ).AsXnaColor();
611
612                    if ( star + k < textureColors.Length )
613                        textureColors[star + k] = starcolor;
614
615                    if ( size % 2 != 0 || size == 2 )
616                        continue;
617
618                    int nextStar = star + k + width;
619
620                    if ( nextStar < ( width * height ) )
621                    {
622                        textureColors[nextStar] = starcolor;
623                    }
624                }
625            }
626
627            //Texture2D newTexture = new Texture2D( Game.GraphicsDevice, width, height, 1, TextureUsage.None, SurfaceFormat.Color );
628            Texture2D newTexture = new Texture2D( Game.GraphicsDevice, width, height, false, SurfaceFormat.Color );
629            newTexture.SetData<XnaColor>( textureColors );
630
631            return new Image( newTexture );
632        }
633
634        /// <summary>
635        /// Luo kuvan tekstistä.
636        /// </summary>
637        /// <param name="text">Teksti josta kuva luodaan</param>
638        /// <param name="font">Fontti</param>
639        /// <param name="textColor">Tekstin väri</param>
640        /// <param name="backgroundColor">Tekstin taustaväri</param>
641        /// <returns>Teksti kuvana</returns>
642        public static Image FromText( string text, Font font, Color textColor, Color backgroundColor )
643        {
644            if ( text == null )
645                text = "";
646
647            var spriteBatch = new SpriteBatch( Game.GraphicsDevice );
648            var device = spriteBatch.GraphicsDevice;
649
650            XnaV2 textDims = font.XnaFont.MeasureString( text );
651            int textw = ( textDims.X > 1 ) ? Convert.ToInt32( textDims.X ) : 1;
652            int texth = ( textDims.Y > 1 ) ? Convert.ToInt32( textDims.Y ) : 1;
653
654            //RenderTarget2D rt = new RenderTarget2D( device, textw, texth, 1, device.DisplayMode.Format );
655            RenderTarget2D rt = new RenderTarget2D( device, textw, texth, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8 );
656
657            //device.SetRenderTarget( 0, rt );
658            device.SetRenderTarget( rt );
659            device.Clear( ClearOptions.Target | ClearOptions.DepthBuffer, backgroundColor.AsXnaColor(), 1.0f, 0 );
660
661            spriteBatch.Begin();
662            spriteBatch.DrawString( font.XnaFont, text, XnaV2.Zero, textColor.AsXnaColor() );
663            spriteBatch.End();
664
665            //device.SetRenderTarget( 0, null );
666            device.SetRenderTarget( null );
667
668            //return new Image( rt.GetTexture() );
669            return new Image( rt );
670        }
671
672        /// <summary>
673        /// Piirtää tekstiä kuvan päälle.
674        /// </summary>
675        /// <param name="img">Kuva jonka päälle piirretään</param>
676        /// <param name="text">Piirrettävä teksti</param>
677        /// <param name="position">Piirtokohta (origo keskellä kuvaa)</param>
678        /// <param name="font">Fontti</param>
679        /// <param name="textColor">Tekstin väri</param>
680        /// <param name="backgroundColor">Tekstin taustaväri</param>
681        /// <returns>Kuva tekstin kanssa</returns>
682        public static Image DrawTextOnImage( Image img, string text, Vector position, Font font, Color textColor, Color backgroundColor )
683        {
684            if ( text == null )
685                text = "";
686
687            var spriteBatch = new SpriteBatch( Game.GraphicsDevice );
688            var device = spriteBatch.GraphicsDevice;
689
690            XnaV2 textDims = font.XnaFont.MeasureString( text );
691            int textw = ( textDims.X > 1 ) ? Convert.ToInt32( textDims.X ) : 1;
692            int texth = ( textDims.Y > 1 ) ? Convert.ToInt32( textDims.Y ) : 1;
693
694            //RenderTarget2D rt = new RenderTarget2D( device, textw, texth, 1, device.DisplayMode.Format );
695            RenderTarget2D rt = new RenderTarget2D( device, img.Width, img.Height, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8 );
696
697            //device.SetRenderTarget( 0, rt );
698            device.SetRenderTarget( rt );
699            device.Clear( ClearOptions.Target | ClearOptions.DepthBuffer, backgroundColor.AsXnaColor(), 1.0f, 0 );
700
701            float xpos = 0.5f * ( img.Width - textw ) + (float)position.X;
702            float ypos = 0.5f * ( img.Height - texth ) - (float)position.Y;
703
704            spriteBatch.Begin();
705            spriteBatch.Draw( img.XNATexture, rt.Bounds, XnaColor.White );
706            spriteBatch.DrawString( font.XnaFont, text, new XnaV2( xpos, ypos ), textColor.AsXnaColor() );
707            spriteBatch.End();
708
709            //device.SetRenderTarget( 0, null );
710            device.SetRenderTarget( null );
711
712
713            //return new Image( rt.GetTexture() );
714            return new Image( rt );
715        }
716
717        /// <summary>
718        /// Piirtää tekstiä kuvan päälle keskelle kuvaa.
719        /// </summary>
720        /// <param name="img">Kuva jonka päälle piirretään</param>
721        /// <param name="text">Piirrettävä teksti</param>
722        /// <param name="font">Fontti</param>
723        /// <param name="textColor">Tekstin väri</param>
724        /// <returns>Kuva tekstin kanssa</returns>
725        public static Image DrawTextOnImage( Image img, string text, Font font, Color textColor )
726        {
727            return DrawTextOnImage( img, text, Vector.Zero, font, textColor, Jypeli.Color.Transparent );
728        }
729
730        /// <summary>
731        /// Luo pystysuuntaisen liukuväritetyn kuvan.
732        /// </summary>
733        /// <param name="imageWidth">kuvan leveys.</param>
734        /// <param name="imageHeight">kuvan korkeus.</param>
735        /// <param name="lowerColor">Alareunassa käytettävä väri.</param>
736        /// <param name="upperColor">Yläreunassa käytettävä väri.</param>
737        /// <returns>Väritetty kuva.</returns>
738        public static Image FromGradient( int imageWidth, int imageHeight, Color lowerColor, Color upperColor )
739        {
740            XnaColor lower = lowerColor.AsXnaColor();
741            XnaColor upper = upperColor.AsXnaColor();
742            XnaColor[] textureColors = new XnaColor[imageWidth * imageHeight];
743            int i = 0;
744
745            for ( int ver = 0; ver < imageHeight; ver++ )
746            {
747                for ( int hor = 0; hor < imageWidth; hor++ )
748                {
749                    textureColors[i++] = XnaColor.Lerp( upper, lower, ( (float)ver / (float)imageHeight ) );
750                }
751            }
752
753            Texture2D newTexture = new Texture2D( Game.GraphicsDevice, imageWidth, imageHeight, false, SurfaceFormat.Color );
754            newTexture.SetData<XnaColor>( textureColors );
755            return new Image( newTexture );
756        }
757
758        /// <summary>
759        /// Luo yksivärisen kuvan.
760        /// </summary>
761        /// <param name="imageWidth">Kuvan leveys.</param>
762        /// <param name="imageHeight">Kuvan korkeus.</param>
763        /// <param name="color">Kuvan väri.</param>
764        /// <returns>Väritetty kuva.</returns>
765        public static Image FromColor( int imageWidth, int imageHeight, Color color )
766        {
767            return Image.FromGradient( imageWidth, imageHeight, color, color );
768        }
769
770        private static XnaColor[] MirrorLine( XnaColor[] scanline, int width )
771        {
772            XnaColor[] res = new XnaColor[width];
773            int l = 0;
774            int r = width - 1;
775
776            while ( l < r )
777            {
778                res[l] = scanline[r];
779                res[r] = scanline[l];
780                l++; r--;
781            }
782
783            if ( l == r )
784            {
785                // Center pixel
786                res[l] = scanline[l];
787            }
788
789            return res;
790        }
791
792        /// <summary>
793        /// Peilaa kuvan X-suunnassa.
794        /// </summary>
795        /// <param name="image">Peilattava kuva.</param>
796        /// <returns>Peilattu kuva.</returns>
797        public static Image Mirror( Image image )
798        {
799            Texture2D newTex = new Texture2D( image.XNATexture.GraphicsDevice, image.Width, image.Height, false, image.XNATexture.Format );
800            XnaColor[] scanline = new XnaColor[image.Width];
801            var scanRect = new XnaRectangle( 0, 0, image.Width, 1 );
802
803            for ( scanRect.Y = 0; scanRect.Y < image.Height; scanRect.Y++ )
804            {
805                image.XNATexture.GetData<XnaColor>( 0, scanRect, scanline, 0, image.Width );
806                scanline = MirrorLine( scanline, image.Width );
807                newTex.SetData<XnaColor>( 0, scanRect, scanline, 0, image.Width );
808            }
809
810            return new Image( newTex );
811        }
812
813        /// <summary>
814        /// Peilaa kuvat X-suunnassa.
815        /// </summary>
816        /// <param name="images">Peilattavat kuvat.</param>
817        /// <returns>Peilatut kuvat.</returns>
818        public static Image[] Mirror( Image[] images )
819        {
820            Image[] result = new Image[images.Length];
821            for ( int i = 0; i < images.Length; i++ )
822                result[i] = Mirror( images[i] );
823            return result;
824        }
825
826        /// <summary>
827        /// Peilaa kuvan Y-suunnassa.
828        /// </summary>
829        /// <param name="image">Peilattava kuva.</param>
830        /// <returns>Peilattu kuva.</returns>
831        public static Image Flip( Image image )
832        {
833            Texture2D newTex = new Texture2D( image.XNATexture.GraphicsDevice, image.Width, image.Height, false, image.XNATexture.Format );
834            XnaColor[] scanlineUpper = new XnaColor[image.Width];
835            XnaColor[] scanlineLower = new XnaColor[image.Width];
836            var scanRectUpper = new XnaRectangle( 0, 0, image.Width, 1 );
837            var scanRectLower = new XnaRectangle( 0, 0, image.Width, 1 );
838
839            for ( int i = 0; i < image.Height / 2; i++ )
840            {
841                scanRectUpper.Y = i;
842                scanRectLower.Y = image.Height - 1 - i;
843
844                image.XNATexture.GetData<XnaColor>( 0, scanRectUpper, scanlineUpper, 0, image.Width );
845                image.XNATexture.GetData<XnaColor>( 0, scanRectLower, scanlineLower, 0, image.Width );
846
847                newTex.SetData<XnaColor>( 0, scanRectUpper, scanlineLower, 0, image.Width );
848                newTex.SetData<XnaColor>( 0, scanRectLower, scanlineUpper, 0, image.Width );
849            }
850
851            if ( image.Height % 2 == 1 )
852            {
853                // Center line
854                scanRectUpper.Y = image.Height / 2;
855                image.XNATexture.GetData<XnaColor>( 0, scanRectUpper, scanlineUpper, 0, image.Width );
856                newTex.SetData<XnaColor>( 0, scanRectUpper, scanlineUpper, 0, image.Width );
857            }
858
859            return new Image( newTex );
860        }
861
862        /// <summary>
863        /// Peilaa kuvat Y-suunnassa.
864        /// </summary>
865        /// <param name="images">Peilattavat kuvat.</param>
866        /// <returns>Peilatut kuvat.</returns>
867        public static Image[] Flip( Image[] images )
868        {
869            Image[] result = new Image[images.Length];
870            for ( int i = 0; i < images.Length; i++ )
871                result[i] = Flip( images[i] );
872            return result;
873        }
874
875        /// <summary>
876        /// Värittää kuvan.
877        /// </summary>
878        /// <param name="image">Väritettävä kuva.</param>
879        /// <param name="color">Väri, jolla väritetään.</param>
880        /// <returns>Väritetty kuva.</returns>
881        public static Image Color( Image image, Color color )
882        {
883            Texture2D newTex = new Texture2D( image.XNATexture.GraphicsDevice, image.Width, image.Height, false, image.XNATexture.Format );
884            XnaColor[] scanline = new XnaColor[image.Width];
885            var scanRect = new XnaRectangle( 0, 0, image.Width, 1 );
886            XnaColor xnaColor = color.AsXnaColor();
887
888            for ( scanRect.Y = 0; scanRect.Y < image.Height; scanRect.Y++ )
889            {
890                image.XNATexture.GetData<XnaColor>( 0, scanRect, scanline, 0, image.Width );
891
892                for ( int i = 0; i < image.Width; i++ )
893                {
894                    if ( scanline[i].A < 255 )
895                    {
896                        scanline[i].R = (byte)( ( 255 - scanline[i].A ) * xnaColor.R + scanline[i].A * scanline[i].R );
897                        scanline[i].G = (byte)( ( 255 - scanline[i].A ) * xnaColor.G + scanline[i].A * scanline[i].G );
898                        scanline[i].B = (byte)( ( 255 - scanline[i].A ) * xnaColor.B + scanline[i].A * scanline[i].B );
899
900                        if ( scanline[i].A > 10 )
901                        {
902                            scanline[i].A = color.AlphaComponent;
903                        }
904                    }
905                }
906
907                newTex.SetData<XnaColor>( 0, scanRect, scanline, 0, image.Width );
908            }
909
910            return new Image( newTex );
911        }
912
913        /// <summary>
914        /// Värittää kuvat.
915        /// </summary>
916        /// <param name="images">Väritettävät kuvat.</param>
917        /// <param name="color">Väri, jolla väritetään.</param>
918        /// <returns>Väritetyt kuvat.</returns>
919        public static Image[] Color( Image[] images, Color color )
920        {
921            Image[] result = new Image[images.Length];
922            for ( int i = 0; i < images.Length; i++ )
923                result[i] = Color( images[i], color );
924            return result;
925        }
926
927        public static Image Color( Image image, byte alpha )
928        {
929            Texture2D newTex = new Texture2D( image.XNATexture.GraphicsDevice, image.Width, image.Height, false, image.XNATexture.Format );
930            XnaColor[] scanline = new XnaColor[image.Width];
931            var scanRect = new XnaRectangle( 0, 0, image.Width, 1 );
932
933            for ( scanRect.Y = 0; scanRect.Y < image.Height; scanRect.Y++ )
934            {
935                image.XNATexture.GetData<XnaColor>( 0, scanRect, scanline, 0, image.Width );
936                for ( int i = 0; i < image.Width; i++ )
937                {
938                    scanline[i].A = alpha;
939                }
940                newTex.SetData<XnaColor>( 0, scanRect, scanline, 0, image.Width );
941            }
942            return new Image( newTex );
943        }
944
945        public static Image TileHorizontal( Image left, Image right )
946        {
947            if ( left.Height != right.Height ) throw new InvalidOperationException( "Cannot tile two images with different height" );
948
949            left.DoInitTexture();
950            right.DoInitTexture();
951
952            XnaRectangle leftRect = new XnaRectangle( 0, 0, left.Width, left.Height );
953            XnaRectangle rightSrc = new XnaRectangle( 0, 0, right.Width, right.Height );
954            XnaRectangle rightDest = new XnaRectangle( left.Width, 0, right.Width, right.Height );
955
956            Image tiled = new Image( left.Width + right.Width, left.Height );
957            tiled.InitTexture += delegate { CopyData( tiled, left, leftRect, leftRect ); };
958            tiled.InitTexture += delegate { CopyData( tiled, right, rightDest, rightSrc ); };
959
960            return tiled;
961        }
962
963        public static Image TileVertical( Image top, Image bottom )
964        {
965            if ( top.Width != bottom.Width ) throw new InvalidOperationException( "Cannot tile two images with different width" );
966
967            top.DoInitTexture();
968            bottom.DoInitTexture();
969
970            XnaRectangle topRect = new XnaRectangle( 0, 0, top.Width, top.Height );
971            XnaRectangle botSrc = new XnaRectangle( 0, 0, bottom.Width, bottom.Height );
972            XnaRectangle botDest = new XnaRectangle( 0, top.Height, bottom.Width, bottom.Height );
973
974            Image tiled = new Image( top.Width, top.Height + bottom.Height );
975            tiled.InitTexture += delegate { CopyData( tiled, top, topRect, topRect ); };
976            tiled.InitTexture += delegate { CopyData( tiled, bottom, botDest, botSrc ); };
977
978            return tiled;
979        }
980
981        #endregion
982
983        public Image Area( int left, int top, int right, int bottom )
984        {
985            int width = right - left;
986            int height = bottom - top;
987
988            if ( width <= 0 ) throw new ArgumentException( "Left coordinate must be less than right coordinate" );
989            if ( height <= 0 ) throw new ArgumentException( "Top coordinate must be less than bottom coordinate" );
990
991            XnaRectangle srcRect = new XnaRectangle( left, top, width, height );
992            XnaRectangle destRect = new XnaRectangle( 0, 0, width, height );
993
994            Image areaImage = new Image( width, height );
995            areaImage.parentImage = this;
996            areaImage.parentRectangle = srcRect;
997            areaImage.InitTexture += delegate { CopyData( areaImage, this, destRect, srcRect ); };
998            return areaImage;
999        }
1000
1001        public void Fill( Color backColor )
1002        {
1003            DoInitTexture();
1004            InvalidateAsset();
1005
1006            XnaRectangle rect = new XnaRectangle( 0, 0, xnaTexture.Width, 1 );
1007            Color[] scanline = new Color[xnaTexture.Width];
1008
1009            for ( int i = 0; i < xnaTexture.Width; i++ )
1010                scanline[i] = backColor;
1011
1012            for ( rect.Y = 0; rect.Y < xnaTexture.Height; rect.Y++ )
1013                xnaTexture.SetData<Color>( 0, rect, scanline, 0, xnaTexture.Width );
1014
1015            UpdateTexture();
1016        }
1017
1018        /// <summary>
1019        /// Korvaa värin toisella värillä.
1020        /// </summary>
1021        /// <param name="src">Korvattava väri.</param>
1022        /// <param name="dest">Väri jolla korvataan.</param>
1023        /// <param name="tolerance">Kuinka paljon korvattava väri voi poiketa annetusta.</param>
1024        /// <param name="blend">Sävytetäänkö korvattavaa väriä sen mukaan kuinka kaukana se on alkuperäisestä väristä</param>
1025        /// <param name="blendAlpha">Vaaditaanko täsmälleen sama läpinäkyvyys ennen kuin korvataan</param>
1026        public void ReplaceColor( Color src, Color dest, double tolerance, bool blend, bool exactAlpha = false )
1027        {
1028            XnaColor srcColor = src.AsXnaColor();
1029            XnaColor destColor = dest.AsXnaColor();
1030            Converter<XnaColor, XnaColor> op = delegate( XnaColor c )
1031            {
1032                if ( exactAlpha && c.A != srcColor.A )
1033                    return c;
1034
1035                if ( JyColor.Distance( c, srcColor ) <= tolerance )
1036                {
1037                    if ( !blend ) return destColor;
1038                    Vector3 srcDist = new Vector3( c.R - srcColor.R, c.G - srcColor.G, c.B - srcColor.B );
1039                    return new XnaColor( destColor.ToVector3() + srcDist );
1040                }
1041
1042                return c;
1043            };
1044
1045            ApplyPixelOperation( op );
1046        }
1047
1048        /// <summary>
1049        /// Korvaa värin toisella värillä.
1050        /// </summary>
1051        /// <param name="src">Korvattava väri</param>
1052        /// <param name="dest">Väri jolla korvataan</param>
1053        public void ReplaceColor( Color src, Color dest )
1054        {
1055            XnaColor srcColor = src.AsXnaColor();
1056            XnaColor destColor = dest.AsXnaColor();
1057            Converter<XnaColor, XnaColor> op = delegate( XnaColor c )
1058            {
1059                return c == srcColor ? destColor : c;
1060            };
1061
1062            ApplyPixelOperation( op );
1063        }
1064
1065        /// <summary>
1066        /// Palauttaa kuvan jpeg-muodossa, jossa se voidaan esimerkiksi tallentaa
1067        /// DataStorage.Export -metodilla.
1068        /// </summary>
1069        /// <returns></returns>
1070        public Stream AsJpeg()
1071        {
1072            DoInitTexture();
1073            MemoryStream jpegStream = new MemoryStream();
1074            XNATexture.SaveAsJpeg( jpegStream, Width, Height );
1075            jpegStream.Seek( 0, SeekOrigin.Begin );
1076            return jpegStream;
1077        }
1078
1079        /// <summary>
1080        /// Palauttaa kuvan png-muodossa, jossa se voidaan esimerkiksi tallentaa
1081        /// DataStorage.Export -metodilla.
1082        /// </summary>
1083        /// <returns></returns>
1084        public Stream AsPng()
1085        {
1086            DoInitTexture();
1087            MemoryStream pngStream = new MemoryStream();
1088            XNATexture.SaveAsPng( pngStream, Width, Height );
1089            pngStream.Seek( 0, SeekOrigin.Begin );
1090            return pngStream;
1091        }
1092    }
1093}
1094
Note: See TracBrowser for help on using the repository browser.