2010-05-19 50 views
9

Tôi đã nhìn xung quanh rất nhiều và các phương pháp duy nhất mà tôi đã tìm thấy để tạo một Texture2D từ một Bitmap là:Có cách thay thế nhanh nào để tạo Texture2D từ đối tượng Bitmap trong XNA không?

using (MemoryStream s = new MemoryStream()) 
{ 
    bmp.Save(s, System.Drawing.Imaging.ImageFormat.Png); 
    s.Seek(0, SeekOrigin.Begin); 
    Texture2D tx = Texture2D.FromFile(device, s); 
} 

Texture2D tx = new Texture2D(device, bmp.Width, bmp.Height, 
         0, TextureUsage.None, SurfaceFormat.Color); 
tx.SetData<byte>(rgbValues, 0, rgbValues.Length, SetDataOptions.NoOverwrite); 

đâu rgbValues ​​là một mảng byte chứa của bitmap dữ liệu pixel ở định dạng ARGB 32 bit.

Câu hỏi của tôi là, có cách tiếp cận nào nhanh hơn mà tôi có thể thử không?

Tôi đang viết trình chỉnh sửa bản đồ phải đọc trong hình ảnh định dạng tùy chỉnh (lát bản đồ) và chuyển đổi chúng thành kết cấu Texture2D để hiển thị. Phiên bản trước của trình soạn thảo, là một triển khai C++, đã chuyển đổi các hình ảnh đầu tiên thành các ảnh bitmap và sau đó chuyển thành các họa tiết được vẽ bằng DirectX. Tôi đã thử cách tiếp cận tương tự ở đây, tuy nhiên cả hai phương pháp trên đều đáng kể quá chậm. Để tải vào bộ nhớ, tất cả các họa tiết cần thiết cho bản đồ sẽ là cách tiếp cận đầu tiên ~ 250 giây và cách tiếp cận thứ hai ~ 110 giây trên máy tính hợp lý (để so sánh, mã C++ mất khoảng 5 giây). Nếu có một phương pháp để chỉnh sửa dữ liệu của một kết cấu trực tiếp (chẳng hạn như với phương thức LockBits của lớp Bitmap) thì tôi sẽ có thể chuyển đổi các hình ảnh định dạng tùy chỉnh thẳng thành một Texture2D và hy vọng tiết kiệm thời gian xử lý.

Mọi trợ giúp sẽ được đánh giá rất nhiều.

Cảm ơn

Trả lời

9

Bạn muốn LockBits? Bạn nhận được LockBits.

Trong triển khai của mình, tôi đã chuyển trong GraphicsDevice từ người gọi để tôi có thể làm cho phương thức này là chung và tĩnh.

public static Texture2D GetTexture2DFromBitmap(GraphicsDevice device, Bitmap bitmap) 
{ 
    Texture2D tex = new Texture2D(device, bitmap.Width, bitmap.Height, 1, TextureUsage.None, SurfaceFormat.Color); 

    BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat); 

    int bufferSize = data.Height * data.Stride; 

    //create data buffer 
    byte[] bytes = new byte[bufferSize];  

    // copy bitmap data into buffer 
    Marshal.Copy(data.Scan0, bytes, 0, bytes.Length); 

    // copy our buffer to the texture 
    tex.SetData(bytes); 

    // unlock the bitmap data 
    bitmap.UnlockBits(data); 

    return tex; 
} 
+1

Không nên là "từ người gọi" thay vì "callee"? Từ mục đích của người sử dụng mã này, đó là hàm này là callee. – drxzcl

+0

Cảm ơn phản hồi của bạn, tuy nhiên tôi có nghĩa là trong câu hỏi ban đầu của tôi rằng tôi đã thử phương pháp này - sử dụng SetData(). Tôi chạy một số phân tích sâu hơn, và để tạo ra các Bitmap bằng cách sử dụng LockBits mất 2,7 giây. Khi tôi thêm vào mã đó, đối với mỗi Bitmap được tạo: Texture2D tx = new Texture2D (thiết bị, bmp.Width, bmp.Height, 0, TextureUsage.None, SurfaceFormat.Color); Nó tăng thời gian cần thiết lên 74 giây !! Chỉ cần tạo một kết cấu trống cho mỗi bitmap, và thậm chí không điền vào nó. Loại thời gian tải này không thể chấp nhận được đối với trò chơi 2D. Tôi không nghĩ rằng XNA sẽ chậm như vậy. :/ –

+0

@Ranieri, cảm ơn - đã sửa! – bufferz

3

Tôi thấy rằng tôi phải chỉ định PixelFormat là .Format32bppArgb khi sử dụng LockBits khi bạn đề xuất lấy hình ảnh webcam.

 BitmapData bmd = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), 
      System.Drawing.Imaging.ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 
     int bufferSize = bmd.Height * bmd.Stride; 
     //create data buffer 
     byte[] bytes = new byte[bufferSize]; 
     // copy bitmap data into buffer 
     Marshal.Copy(bmd.Scan0, bytes, 0, bytes.Length); 

     // copy our buffer to the texture 
     Texture2D t2d = new Texture2D(_graphics.GraphicsDevice, bmp.Width, bmp.Height, 1, TextureUsage.None, SurfaceFormat.Color); 
     t2d.SetData<byte>(bytes); 
     // unlock the bitmap data 
     bmp.UnlockBits(bmd); 
     return t2d; 
7

họ đã thay đổi định dạng từ bgra thành rgba trong XNA 4.0, do đó phương pháp này mang lại màu sắc lạ, kênh màu đỏ và màu xanh cần phải được chuyển. Đây là một phương pháp tôi đã viết đó là siêu nhanh! (tải 1500x 256x256 pixel kết cấu trong khoảng 3 giây).

private Texture2D GetTexture(GraphicsDevice dev, System.Drawing.Bitmap bmp) 
    { 
     int[] imgData = new int[bmp.Width * bmp.Height]; 
     Texture2D texture = new Texture2D(dev, bmp.Width, bmp.Height); 

     unsafe 
     { 
      // lock bitmap 
      System.Drawing.Imaging.BitmapData origdata = 
       bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat); 

      uint* byteData = (uint*)origdata.Scan0; 

      // Switch bgra -> rgba 
      for (int i = 0; i < imgData.Length; i++) 
      { 
       byteData[i] = (byteData[i] & 0x000000ff) << 16 | (byteData[i] & 0x0000FF00) | (byteData[i] & 0x00FF0000) >> 16 | (byteData[i] & 0xFF000000);       
      }     

      // copy data 
      System.Runtime.InteropServices.Marshal.Copy(origdata.Scan0, imgData, 0, bmp.Width * bmp.Height); 

      byteData = null; 

      // unlock bitmap 
      bmp.UnlockBits(origdata); 
     } 

     texture.SetData(imgData); 

     return texture; 
    } 
+2

Đẹp! Nhưng bạn không nên sửa đổi dữ liệu bitmap vì bạn đã khóa nó với ReadOnly? Bạn chỉ có thể sao chép nó trước rồi thực hiện chuyển đổi. Hoặc thậm chí đơn giản hơn, chỉ cần thực hiện chuyển đổi và sao chép trong một bước! (Gán cho imgData [i] thay vì byteData [i], thì bạn không cần bản sao nào cả!) – Cameron

+0

Ồ, cảm ơn, nó làm việc cho tôi. –

+1

+1 Điều này rất tiện dụng, mặc dù (như @Cameron đề xuất) sẽ tốt hơn nếu bạn không thay đổi bitmap gốc, nhưng thay vào đó làm việc trực tiếp trên mảng 'imgData' (bạn sẽ không phải sử dụng 'unsafe' trong trường hợp đó). – Groo

1

Khi tôi đọc câu hỏi này lần đầu tiên, tôi cho rằng đó là hiệu suất là SetData. Tuy nhiên, đọc nhận xét của OP trong câu trả lời hàng đầu, anh ấy dường như đang phân bổ một số của lớn Texture2D's.

Thay vào đó, hãy xem xét việc có một hồ bơi của Texture2D, mà bạn phân bổ khi cần thiết, trở lại hồ bơi khi không còn cần thiết.

Lần đầu tiên mỗi tệp kết cấu là cần thiết (hoặc trong phần "tải trước" khi bắt đầu quy trình của bạn, tùy thuộc vào nơi bạn muốn trì hoãn), tải từng tệp vào một mảng byte[]. (Lưu trữ các mảng byte[] này trong LRU Cache - trừ khi bạn chắc chắn bạn có đủ bộ nhớ để giữ chúng luôn luôn.) Sau đó, khi bạn cần một trong các họa tiết đó, hãy lấy một trong các kết cấu hồ bơi, (phân bổ mới một, nếu không có kích thước thích hợp có sẵn), SetData từ mảng byte của bạn - viola, bạn có một kết cấu.

[Tôi đã để lại các chi tiết quan trọng, chẳng hạn như nhu cầu kết cấu được liên kết với một thiết bị cụ thể - nhưng bạn có thể xác định bất kỳ nhu cầu nào từ các tham số đến phương pháp bạn đang gọi. Điểm tôi tạo ra là giảm thiểu các cuộc gọi đến hàm tạo Texture2D, đặc biệt nếu bạn có nhiều hoạ tiết lớn.]

Nếu bạn thực sự ưa thích và đang xử lý nhiều hoạ tiết khác nhau, bạn cũng có thể áp dụng LRU Cache nguyên tắc cho hồ bơi. Cụ thể, theo dõi tổng số byte của các đối tượng "miễn phí" được giữ trong hồ bơi của bạn. Nếu tổng số đó vượt quá một số ngưỡng bạn đặt (có thể kết hợp với tổng số đối tượng "miễn phí"), sau đó yêu cầu tiếp theo, vứt bỏ các mục trong hồ bơi miễn phí lâu nhất (có kích thước sai hoặc các thông số sai khác). ngưỡng không gian bộ nhớ "bị lãng phí".

BTW, bạn có thể làm tốt chỉ cần theo dõi ngưỡng và bỏ đi tất cả đối tượng miễn phí khi vượt quá ngưỡng. Nhược điểm là một trục trặc tạm thời trong lần tiếp theo bạn phân bổ một loạt các kết cấu mới - mà bạn có thể cải thiện nếu bạn có thông tin về kích thước bạn nên giữ lại. Nếu điều đó không đủ tốt, thì bạn cần LRU.

+0

Mặc dù đã 8 năm rồi, tôi đánh giá cao việc bạn viết câu trả lời cho câu hỏi của tôi. Tôi nghĩ rằng cách tiếp cận của bạn thực sự có thể làm việc tốt nếu cuộc gọi đến constructor Texture2D là nút cổ chai. –

Các vấn đề liên quan