2010-03-23 7 views
1

Alors, voici mon problème. J'ai une image dont j'ai besoin pour rétrécir. L'image d'origine est un PNG en niveaux de gris, ce qui n'est pas un gros problème sauf que lorsque je le rétrécis, les imprimantes d'étiquettes thermiques ramassent les artefacts et les impriment sur l'étiquette. Donc, ce que je faisais était de changer l'image en noir blanc & (Format1bppIndexed) avant le redimensionnement, comme ceci:OutOfMemoryException avec Image.Clone - Uniquement sur Windows 2003

Dim bte() As Byte = System.Convert.FromBase64String(imgStr) 
Dim ms As New IO.MemoryStream(bte) 
Dim bmp As New System.Drawing.Bitmap(ms) 
Dim monoImage As Drawing.Bitmap = New Drawing.Bitmap(1200, 1800, Drawing.Imaging.PixelFormat.Format1bppIndexed) 
Dim rect As New Drawing.Rectangle(0, 0, 1200, 1800) 
monoImage = bmp.Clone(rect, Drawing.Imaging.PixelFormat.Format1bppIndexed) 

Et puis je redimensionner. Ce code fonctionne correctement sur mon ordinateur Windows 7, mais lorsque je l'exécute sur la machine Windows 2003 Server qu'il appelle home, il renvoie toujours une exception OutOfMemoryException lorsqu'il touche la ligne bmp.Clone.

Des idées sur ce qui se passe, ou peut-être une meilleure solution pour convertir l'image en B & W?

+0

la quantité de mémoire sur votre machine Win7 vs Win 2003? Quelle est la taille du fichier de l'image dont nous parlons? –

+0

Ce poste pourrait vous aider: http://stackoverflow.com/questions/1836632/convert-image-to-1-bpp-bitmap-in-net-compact-framework –

+0

Bryan - Ma machine est x64 avec 6Go et le serveur est de 32 bits avec 4 Go, devrait être beaucoup dans les deux cas, car le fichier est seulement 120 Ko de taille. – jeffrapp

Répondre

2

Voici un code source converti à partir this article:

Option Explicit On 
Option Strict On 

Public Class BitmapEncoder 
    ''' <summary> 
    ''' Copies a bitmap into a 1bpp bitmap of the same dimensions, fast 
    ''' </summary> 
    ''' <param name="b">original bitmap</param> 
    ''' <returns>a 1bpp copy of the bitmap</returns> 
    Public Shared Function ConvertBitmapTo1bpp(ByVal b As System.Drawing.Bitmap) As System.Drawing.Bitmap 
     ' Plan: built into Windows GDI is the ability to convert 
     ' bitmaps from one format to another. Most of the time, this 
     ' job is actually done by the graphics hardware accelerator card 
     ' and so is extremely fast. The rest of the time, the job is done by 
     ' very fast native code. 
     ' We will call into this GDI functionality from C#. Our plan: 
     ' (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed) 
     ' (2) Create a GDI monochrome hbitmap 
     ' (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above) 
     ' (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed) 

     Dim w As Integer = b.Width, h As Integer = b.Height 
     Dim hbm As IntPtr = b.GetHbitmap() 
     ' this is step (1) 
     ' 
     ' Step (2): create the monochrome bitmap. 
     ' "BITMAPINFO" is an interop-struct which we define below. 
     ' In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs 
     Dim bmi As New BITMAPINFO() 
     bmi.biSize = 40 
     ' the size of the BITMAPHEADERINFO struct 
     bmi.biWidth = w 
     bmi.biHeight = h 
     bmi.biPlanes = 1 
     ' "planes" are confusing. We always use just 1. Read MSDN for more info. 
     bmi.biBitCount = CShort(1) 
     ' ie. 1bpp or 8bpp 
     bmi.biCompression = BI_RGB 
     ' ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes 
     bmi.biSizeImage = CUInt((((w + 7) And &HFFFFFFF8) * h/8)) 
     bmi.biXPelsPerMeter = 1000000 
     ' not really important 
     bmi.biYPelsPerMeter = 1000000 
     ' not really important 
     ' Now for the colour table. 
     Dim ncols As UInteger = CUInt(1) << 1 
     ' 2 colours for 1bpp; 256 colours for 8bpp 
     bmi.biClrUsed = ncols 
     bmi.biClrImportant = ncols 
     bmi.cols = New UInteger(255) {} 
     ' The structure always has fixed size 256, even if we end up using fewer colours 

     bmi.cols(0) = MAKERGB(0, 0, 0) 
     bmi.cols(1) = MAKERGB(255, 255, 255) 
     ' 
     ' Now create the indexed bitmap "hbm0" 
     Dim bits0 As IntPtr 
     ' not used for our purposes. It returns a pointer to the raw bits that make up the bitmap. 
     Dim hbm0 As IntPtr = CreateDIBSection(IntPtr.Zero, bmi, DIB_RGB_COLORS, bits0, IntPtr.Zero, 0) 
     ' 
     ' Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap 
     ' GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC". 
     Dim sdc As IntPtr = GetDC(IntPtr.Zero) 
     ' First we obtain the DC for the screen 
     ' Next, create a DC for the original hbitmap 
     Dim hdc As IntPtr = CreateCompatibleDC(sdc) 
     SelectObject(hdc, hbm) 
     ' and create a DC for the monochrome hbitmap 
     Dim hdc0 As IntPtr = CreateCompatibleDC(sdc) 
     SelectObject(hdc0, hbm0) 
     ' Now we can do the BitBlt: 
     BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, SRCCOPY) 
     ' Step (4): convert this monochrome hbitmap back into a Bitmap: 
     Dim b0 As System.Drawing.Bitmap = System.Drawing.Bitmap.FromHbitmap(hbm0) 
     ' 
     ' Finally some cleanup. 
     DeleteDC(hdc) 
     DeleteDC(hdc0) 
     ReleaseDC(IntPtr.Zero, sdc) 
     DeleteObject(hbm) 
     DeleteObject(hbm0) 
     ' 
     Return b0 
    End Function 


    Private Shared SRCCOPY As Integer = &HCC0020 
    Private Shared BI_RGB As UInteger = 0 
    Private Shared DIB_RGB_COLORS As UInteger = 0 
    <System.Runtime.InteropServices.DllImport("gdi32.dll")> _ 
    Private Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean 
    End Function 

    <System.Runtime.InteropServices.DllImport("user32.dll")> _ 
    Private Shared Function GetDC(ByVal hwnd As IntPtr) As IntPtr 
    End Function 

    <System.Runtime.InteropServices.DllImport("gdi32.dll")> _ 
    Private Shared Function CreateCompatibleDC(ByVal hdc As IntPtr) As IntPtr 
    End Function 

    <System.Runtime.InteropServices.DllImport("user32.dll")> _ 
    Private Shared Function ReleaseDC(ByVal hwnd As IntPtr, ByVal hdc As IntPtr) As Integer 
    End Function 

    <System.Runtime.InteropServices.DllImport("gdi32.dll")> _ 
    Private Shared Function DeleteDC(ByVal hdc As IntPtr) As Integer 
    End Function 

    <System.Runtime.InteropServices.DllImport("gdi32.dll")> _ 
    Private Shared Function SelectObject(ByVal hdc As IntPtr, ByVal hgdiobj As IntPtr) As IntPtr 
    End Function 

    <System.Runtime.InteropServices.DllImport("gdi32.dll")> _ 
    Private Shared Function BitBlt(ByVal hdcDst As IntPtr, ByVal xDst As Integer, ByVal yDst As Integer, ByVal w As Integer, ByVal h As Integer, ByVal hdcSrc As IntPtr, _ 
    ByVal xSrc As Integer, ByVal ySrc As Integer, ByVal rop As Integer) As Integer 
    End Function 


    <System.Runtime.InteropServices.DllImport("gdi32.dll")> _ 
    Private Shared Function CreateDIBSection(ByVal hdc As IntPtr, ByRef bmi As BITMAPINFO, ByVal Usage As UInteger, ByRef bits As IntPtr, ByVal hSection As IntPtr, ByVal dwOffset As UInteger) As IntPtr 
    End Function 

    <System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)> _ 
    Private Structure BITMAPINFO 
     Public biSize As UInteger 
     Public biWidth As Integer, biHeight As Integer 
     Public biPlanes As Short, biBitCount As Short 
     Public biCompression As UInteger, biSizeImage As UInteger 
     Public biXPelsPerMeter As Integer, biYPelsPerMeter As Integer 
     Public biClrUsed As UInteger, biClrImportant As UInteger 
     <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=256)> _ 
     Public cols As UInteger() 
    End Structure 

    Private Shared Function MAKERGB(ByVal r As Integer, ByVal g As Integer, ByVal b As Integer) As UInteger 
     Return CUInt((b And 255)) Or CUInt(((r And 255) << 8)) Or CUInt(((g And 255) << 16)) 
    End Function 
    Private Sub New() 

    End Sub 
End Class 

appel à l'aide:

Dim myFile = "c:\test.jpg" 

    Using Input As New Bitmap(myFile) 
     Using Output = BitmapEncoder.ConvertBitmapTo1bpp(Input) 
      Output.Save("c:\test.bmp") 
     End Using 
    End Using 
+0

Cela fonctionne vraiment! On dirait que beaucoup de code pour faire une tâche si simple;) Merci, Chris! – jeffrapp

+0

Je sais, je pensais que tu étais dingue au début mais ensuite j'ai continué à creuser et j'ai trouvé que tout le monde avait le même problème et la seule solution était de passer au niveau bas. Bizarre. –

1

GDI + messages d'exception sont assez misérable, OutOfMemoryException peuvent être soulevées dans le clone() si le rectangle que vous passez est en dehors de la limites de l'image. Rien à voir avec manquer de mémoire. Ce qui pourrait facilement se produire ici, il est pas probable que le bitmap source est de 1200 x 1800. Faites votre look de code comme ceci:

Dim bte() As Byte = System.Convert.FromBase64String(imgStr) 
Dim ms As New IO.MemoryStream(bte) 
Dim monoImage As Drawing.Bitmap 
Using bmp As New Drawing.Bitmap(ms) 
    Dim rect As New Drawing.Rectangle(0, 0, bmp.Width, bmp.Height) 
    monoImage = bmp.Clone(rect, Drawing.Imaging.PixelFormat.Format1bppIndexed) 
End Using 

Pas si sûr le bitmap résultant serait bien ou est utilisable, a GDI + mauvaise prise en charge des formats de pixels indexés. Win7 a une version différente de gdiplus.dll que Windows 2003. J'ai testé ce code uniquement sur Win7.

+0

Je viens de le faire sur Vista et j'ai reçu le même message d'erreur. J'ai même essayé un rectangle de 1,1,10,10 et j'ai eu une erreur. –

+0

Même ici. Encore une fois, cela fonctionne très bien sur mon ordinateur Win7, mais explose en 2003. – jeffrapp

0

Un problème que je vois est tout de suite que votre code a une fuite de ressource:

Dim monoImage As Drawing.Bitmap = New Drawing.Bitmap(1200, 1800, 
    Drawing.Imaging.PixelFormat.Format1bppIndexed) 
... 
monoImage = bmp.Clone(rect, Drawing.Imaging.PixelFormat.Format1bppIndexed) 

Vous créez une image bitmap flambant neuf et l'attribution à monoImage, puis presque immédiatement jeter sans jamais élimination de l'original Bitmap. Si vous le faites souvent, vous fuyez beaucoup de ressources GDI et pouvez recevoir toutes sortes d'erreurs (y compris les MOO).

il suffit de déplacer la déclaration sur la même ligne que l'affectation « nouveau »:

Dim monoImage As Drawing.Bitmap = bmp.Clone(rect, 
    Drawing.Imaging.PixelFormat.Format1bppIndexed) 

Vous devez également vous assurer que vous disposer l'autre Bitmap et le MemoryStream, et ainsi de suite et ainsi de suite ; tous ces implémenter IDisposable donc juste les envelopper dans Using.

Questions connexes