UNLESS YOU WOULD LIKE TO READ ON, OTHERWISE THE THREAD IS CLOSED AS i HAVE MEANWHILE SOLVED THE PROBLEM ALREADY -- SEE "EDITED" AT BOTTOM PART OF THIS POSTING.
Attached 2 zips contain 24bpp.pcx and 8bpp.pcx respectively, both RLE-compressed.
24bpp.pcx appears to display okay on IrfanView, FreeImage and PhotoShop7. (Remarks: This one happens to be so, other tests had shown it might not always be the case on PhotoShop7)
However, for 8bpp.pcx there is a difference between the display by IrfanView on one hand and that by FreeImage/PhotoShop7 on the other (see images shown below).
Any possible explanation? Likely an error in RLE subroutine?
Appended below is the RLE subroutine used. Please note that PCX files produced by using this subroutine are always displayed perfectly okay on IrfanView, but not so on FreeImage/PhotoShop7 (as said above).
Code:
Private Function CompressPCX(inArr() As Byte, inScanW As Long) As Boolean
Dim arrNew() As Byte
Dim x As Long
Dim K As Long
Dim newPos As Long
Dim mPos As Long
Dim LineCtn As Long
On Error GoTo errHandler
If inScanW Mod 2 <> 0 Then Exit Function 'PCX scanline must be an even number
'Temporarily provide a tentative new array, will adj at the end.
'Notes: (a) If a file dosen't have many "runs", RLE could augment the data size by as much as twice.
'(b) To gain a smaller size, arrange more frequently used colors in early part in palette sequence.
ReDim arrNew(UBound(inArr) * 2)
Do While x <= UBound(inArr)
If x + 1 <= UBound(inArr) Then
Do Until inArr(x) <> inArr(x + 1)
If x + 1 > UBound(inArr) Then Exit Do
'Scanline break reached, exit to handle
If x + 1 > (inScanW * LineCtn) Then Exit Do
'Max of 63 reached? (Max run length in PCX is 63. K is 0-based)
If K = 62 Then Exit Do
x = x + 1
K = K + 1
Loop
End If
If inArr(x) >= 192 Or K > 0 Then
'Since K is limited to max of 62, 193+62 would not execeed 255.
arrNew(newPos) = 193 + K
arrNew(newPos + 1) = inArr(x)
mPos = newPos + 1
newPos = newPos + 2
Else
'If color < 192, then input value directly in a single byte
arrNew(newPos) = inArr(x)
mPos = newPos
newPos = newPos + 1
End If
'Scanline break
If x + 1 = (inScanW * LineCtn) Then
arrNew(newPos) = 0
mPos = newPos
newPos = newPos + 1
LineCtn = LineCtn + 1
End If
If mPos >= UBound(arrNew) Then
GoTo errHandler
End If
K = 0 'Renew max run length counter k, 0-based here
x = x + 1 'Increment byte pointer x
Loop
If mPos > 1 Then
ReDim inArr(mPos)
CopyMemory inArr(0), arrNew(0), mPos + 1
CompressPCX = True
End If
Erase arrNew
Exit Function
errHandler:
End Function
Edited: The whole trouble turns out to be just a typo! By changing "If (x + 1) = UBound(inArr) Then Exit Do" to "If (x + 1) > UBound(inArr) Then Exit Do", the image is displayed alright on FreeImage and PhotoShop7 now.
Tested all okay on AZ Paint Pro, IrfanView, FreeImage and PhotoShop.
.
Last edited by petersen; Jan 25th, 2011 at 04:37 PM.
Reason: Added "Notes: ......" comment
Comment 1. The width does not have to be evenly divisible by 2. The BytesPerLine you include in the header will include an extra byte for the odd-sized image.
Comment 2. The compression can be larger than the image, but one would have to almost try to make it so. If you were to have a checkerboard pattern with only two colors that include the 2 high bits, each color component written would take 2 bytes, or 6 bytes per pixel vs just 3 or less. That's speaking of 24 bpp. And to try to force it in 8 bpp, again a checkerboard pattern referencing palette indexes 254 & 255. Similar result.
I have a far simpler routine for compressing and need to tweak it a bit, but it is short enough to post here. I actually have two routines, one for 256 color and one for 24 bpp. Both are pretty compact. I, personally, do not plan on supporting creation of other bit depths in pcx. I am supporting 1 bpp reading but not writing & still looking for 4 bpp & 16 bpp sample pcx files to see if I will support those too for readding also.
P.S. I haven't looked too close at your code yet. But to prevent cross-over into another line, simply end the counting when you get the last pixel in the line.
Last edited by LaVolpe; Dec 31st, 2010 at 10:55 PM.
Insomnia is just a byproduct of, "It can't be done"
Frankly, if not because of the "old thread" which made me dig into the old stuff, I wouldn't even have the chance to revisit the old code - it looks like a stranger to me by now.
This morning I found that regarding the even number width required, the parameter in the subroutine is in fact the scanline. Therefore, before things are sorted out, I put back the originally posted code lines still. To avoid confusion even to myself, the parameter is now changed from "inW" to "inScanW".
Last edited by petersen; Jan 9th, 2011 at 03:04 PM.
Reason: To remove para no longer relevant, to avoid confusion
Here's a very simple compression routine I'm using for now. If pcx was a more popular image format, I'd optimize to compress better. With the following, no chance of RLE crossing scanlines.
Code:
' rawStream() is passed source image data as 8bit as 2D array. Change code below for 1D arrays
' Palette() is passed 768 byte color array as 3-byte colors, RGB format not BGR
' imgWidth & imgHeight are passed source image size
' bOut will contain the pcx data & you fill in the 128-byte header
' special note: the header's BytesPerLine value is calculated as: imgWidth + (imgWidth And 1)
Dim bOut() As Byte, bEncoded() As Byte
Dim ePtr As Long, X As Long, Y As Long
Dim lCount As Long, bPrev As Byte
'//// uncompressed routine
' size array exactly as needed: 128 header, image data, palette flag, palette
ReDim bOut(0 To 128& + (imgWidth + (imgWidth And 1)) * imgHeight + 768&)
ePtr = 128&
For Y = imgHeight - 1& To 0& Step -1&
For X = 0& To imgWidth - 1&
bOut(ePtr) = rawStream(X, Y)
ePtr = ePtr + 1&
Next
ePtr = ePtr + (imgWidth And 1&)
Next
bOut(ePtr) = &HC: ePtr = ePtr + 1&
CopyMemory bOut(ePtr), Palette(0), 768&
'///// compressed routine
' add buffer for an encoded line. Max size will be 2* width
ReDim bEncoded(0 To (imgWidth + (imgWidth And 1)) * 2)
ReDim bOut(0 To 128& + 768&)
For Y = imgHeight - 1& To 0& Step -1&
ePtr = 0&: lCount = 1&
bPrev = rawStream(0, Y)
For X = 1& To imgWidth - 1&
If rawStream(X, Y) = bPrev Then
If lCount = 63& Then
bEncoded(ePtr) = &HFF: bEncoded(ePtr + 1&) = bPrev
lCount = 0&: ePtr = ePtr + 2&
End If
lCount = lCount + 1&
Else
If lCount = 1& Then
If (bPrev And &HC0) = &HC0 Then
bEncoded(ePtr) = &HC1: bEncoded(ePtr + 1&) = bPrev
ePtr = ePtr + 2&
Else
bEncoded(ePtr) = bPrev: ePtr = ePtr + 1&
End If
Else
bEncoded(ePtr) = lCount Or &HC0: bEncoded(ePtr + 1) = bPrev
ePtr = ePtr + 2&
End If
lCount = 1&
bPrev = rawStream(X, Y)
End If
Next
lCount = lCount + (imgWidth And 1&) ' account for non-even sized widths
If lCount = 1& Then
If (bPrev And &HC0) = &HC0 Then
bEncoded(ePtr) = &HC1: bEncoded(ePtr + 1&) = bPrev
ePtr = ePtr + 2&
Else
bEncoded(ePtr) = bPrev: ePtr = ePtr + 1&
End If
Else
bEncoded(ePtr + 1&) = bPrev
If lCount = 64& Then ' handle improbable scenario
bEncoded(ePtr) = &HFF
bEncoded(ePtr + 2&) = 0&
ePtr = ePtr + 3&
Else
bEncoded(ePtr) = lCount Or &HC0
ePtr = ePtr + 2&
End If
End If
lCount = UBound(bOut) + 1&
ReDim Preserve bOut(0 To lCount + ePtr - 1&)
CopyMemory bOut(lCount - 769&), bEncoded(0), ePtr
Next
ePtr = UBound(bOut) - 768&
Erase bEncoded()
bOut(ePtr) = &HC: ePtr = ePtr + 1&
CopyMemory bOut(ePtr), Palette(0), 768&
I have a routine for 24 bit also, if interested. It is nearly identical to above but writes to 3 (24bit) or 4 (32bit) planes vs. just 1 color plane. Don't even know if 32bpp is recognized by pcx parsers & am going to experiment. But the routine adds no real overhead if 32bit never used
Edited: 24bit routine posted below at #19
Last edited by LaVolpe; Jan 1st, 2011 at 03:08 AM.
Reason: Appending encoded data offset was off by 1: fixed
Insomnia is just a byproduct of, "It can't be done"
Without a ready-to-run sample project, I wouldn't be able to test saving some images and then load them to IrfanView, FreeImage and PhotoShop7 to see whether your subroutine works for me. Because of this, I will stay with my single dimension array to use and do some examination later on (i) as my existing subroutine is 100% okay with IrfanView and basically okay with FreeImage, I don't want to overhaul it to a 2-dimension array just for testing and (ii) pcx users are extremely rare, there is no immediate urgency.
Your choice. The routine I gave you is simple enough to implement without much effort at all.
Supply the routine the 4 pieces of info: PixelData, Width, Height, Palette
Make it return the bOut() array and populate the header in that array.
As for converting the routine to use 1D array... that's elementary.
I offered my routine for possible implementation. And even if you didn't want to use it as is, the logic is so simple to follow; apply it to your compression routine.
P.S. Routine will work because it is about as basic as you can get while compressing.
If you want to see whether they will work in a ready-to-run project; you'll have to wait until I update my Alpha Image Control to use these pcx formats/
Insomnia is just a byproduct of, "It can't be done"
In lieu of a ready-to-go project, here is a pcx created from the routine in post 4.
Note that I simply took the 24bpp source and saved it as a 8bpp bitmap via Paint; hence the pixellation. But the pcx, converted from that source, is identical to the source. If it shows correctly in your apps, then this should help prove validity of the routine.
Insomnia is just a byproduct of, "It can't be done"
PCX is rather untidy, no wonder FreeImage explicitly said that it purposely left "Save" function out, "to force to save to other format". The following are but 2 examples: (a) In the previous thread, I uploaded both uncompressed and compressed 24-BPP image there. All perfectly fine with IrfanView, FreeImage and PhotoShop, except for the uncompressed on PhotoShop, (and obviously also okay with the printer software of AndreiMhz, as confirmed by him). The job was done using the subroutine posted. However, this does not prove that my subroutine is okay, because if I change to load other images, the results would vary. (b) If I load a 24-BPP 1024x674 picture, resize it to a smaller size (program does resize automatically when user drags to resize the viewport) and save it, the saved file is okay on the said 3 testing softwares. However, if I save at the original size, it turns out not every one is okay with it. Now if I load a 8-BPP one (of the same image), yet the results vary differently.
I was not aware of the above, until very recently (brought about by the previous thread). Certainly there is something not quite about the RLE subroutine, or even the bytes preparation subroutine, for me the problem is to narrow down the diagnosis.
Edited: For the uncompressed one mentioned above, the code was posted in previous thread, for the comprssed one the code is in my above posting. IrfanView is most capable, always okay, be it 24 or 8-BPP, compressed or uncompressed, big or smaller images. PhotoShop 7 is very rigid on the other hand.
Last edited by petersen; Dec 31st, 2010 at 05:16 PM.
If I recall your uncompressed 24bit routine, it simply copied the dib bits as is, if the width was evenly divisible, correct? If so, 2 problems with that solution:
1) pcx is RGB not BGR which you get from GetDibBits
2) pcx 24bit is written with all Reds, then Greens, then Blues per scanline, not R,G,B per pixel
But maybe I didn't recall the routine correctly.
Regarding resizing issues: Probably related to the BytesPerLine value you encode in both the header & image data as decoders use that info to build scanlines from decoded data. Different viewers have different ways of decoding the data too. I am playing with 2 methods now. 1st method is flawless because it decodes entire file first, then builds the image. But that is slow IMO. I'd rather decode & build image on the fly which would be close to 2x faster. However, with a multitude of encoding practices, one must contend with things like cross-scanline RLE and cross-channel/plane RLE. In that previous thread, I was able to replicate your images from post #1 above. In doing so, I simply turned off my cross-scanline checks and got the ripple effect. This implies that the pcx viewer doesn't account for such things. Just like other image format viewers (i.e., PNG), some are better than others, some abide by strict formatting, some allow and correct for imperfections. Maybe FreeImage is too strict & InfranView is more adaptable. But one thing should be stated. If encoding using strictness, it should be able to be read by all viewers. That comment won't be surprising to you; you already know I make strides to abide by format specifications to the letter. However; I try to allow malformatted data when reading.
But as you say; there really isn't any black and white rules for encoding/decoding in all scenarios. It's more or less one's interpretation in many areas of pcx and format allows for customization somewhat. Probably hard to find since pcx is kind of a dead format; except in specialized software (i.e., printers, some games). Many develop their own special purpose pcx formats to suit their needs or the needs of their apps. Just because it has a pcx extension doesn't necessarily mean its a true pcx. I'm even going to try to encode 32bpp alpha in the pcx and see if viewers will even read the 24bit portion. If not, then it'll just be a silly experiment.
I'll post my 24bit routine for uncompressed/compressed pcx format. If you find it useful, great. If not, maybe others that happen upon this thread may find it so. Off to tidy up pcx and my Alpha Image Control
Last edited by LaVolpe; Dec 31st, 2010 at 05:32 PM.
Insomnia is just a byproduct of, "It can't be done"
(1) My code posting is still there in the previous thread. For your easy reference I will copy it over below (hopefully some able guy can spot something not quite there and pin-point the correction).
(2) From the said code, you can tell I actually used BRG for PCX. Another angle to look at this is, as I repeatedly mentioned, IrfanView and FreeImage (and the printer software of AndreiMhz, as well as PCX Viewer according to AndrelMhz) all displayed the image obtained thereof perfectly alright. How could they, if I didn't use BGR correctly at that junction?
(3) I would certainly welcome your project when available. If tests show okay, then it would really be of great help to me.
Appended below is the code mentioned in "1" above:
Code:
Private Type RGBTriple
Red As Byte
Green As Byte
Blue As Byte
End Type
Private Type PCXHeader ' 128 bytes
Manufacturer As Byte
Version As Byte
Encoding As Byte ' 1 = RLE
BitsPP As Byte ' 1, 2, 4, 8
xMin As Integer
yMin As Integer
w As Integer
h As Integer
hDPI As Integer
vDPI As Integer
Palette(0 To 15) As RGBTriple
Reserved As Byte
Planes As Byte
BytesPerLine As Integer
PaletteType As Integer
HScreenSize As Integer
VScreenSize As Integer
Reserved2(0 To 53) As Byte
End Type
Private Type RGBQUAD
B As Byte
G As Byte
R As Byte
a As Byte
End Type
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, _
ByVal ByteLen As Long)
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, _
ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, _
ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function GetDIBits Lib "gdi32" (ByVal aHDC As Long, ByVal hBitmap As Long, _
ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpbi As Any, _
ByVal wUsage As Long) As Long
Private Declare Function CreateDIBSection Lib "gdi32" (ByVal hDC As Long, pBitmapInfo As BITMAPINFO, _
ByVal un As Long, ByVal lplpVoid As Long, ByVal handle As Long, ByVal dw As Long) As Long
Private Const DIB_RGB_COLORS = 0
Private Const BI_RGB = 0
Private Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Private Type BITMAPINFO
bmiHeader As BITMAPINFOHEADER
bmiColors As RGBQUAD
End Type
Private Type BGRcolorspace
b As Byte
g As Byte
r As Byte
End Type
Public Function SavePCX(inFileSpec As String, inHDC As Long, inW As Long, inH As Long) As Boolean
Dim tmpPCX As PCXHeader
Dim mHandle As Integer
Dim arrByte() As Byte
Dim BI As BITMAPINFO
Dim tmpBitmap As Long
Dim tmpDC As Long
Dim arrPCXbyte() As Byte
Dim DIBw As Long
Dim PCXw As Long
Dim tmpByte() As Byte
Dim i As Long
If inW * 3 Mod 4 = 0 Then
DIBw = inW * 3
Else
DIBw = (inW * 3 \ 4 + 1) * 4
End If
If inW Mod 2 = 0 Then
PCXw = inW
Else
PCXw = inW + 1
End If
ReDim arrPCXbyte((DIBw) * inH - 1)
With BI.bmiHeader
.biSize = Len(BI.bmiHeader)
.biWidth = inW
.biHeight = inH
.biBitCount = 24
.biCompression = BI_RGB
.biPlanes = 1
End With
tmpDC = CreateCompatibleDC(inHDC)
tmpBitmap = CreateDIBSection(tmpDC, BI, DIB_RGB_COLORS, ByVal 0&, ByVal 0&, ByVal 0&)
SelectObject tmpDC, tmpBitmap
BitBlt tmpDC, 0, 0, BI.bmiHeader.biWidth, BI.bmiHeader.biHeight, inHDC, 0, 0, vbSrcCopy
'Adj for bottom up
BI.bmiHeader.biHeight = (0 - inH)
GetDIBits tmpDC, tmpBitmap, 0, inH, arrPCXbyte(0), BI, DIB_RGB_COLORS
ReDim tmpByte(PCXw * inH * 3 - 1)
'Put to PCX sequence
If inW * 3 <> DIBw Then
For i = 0 To inH - 1
CopyMemory tmpByte(i * PCXw * 3), arrPCXbyte(i * DIBw), PCXw * 3
Next i
Else
CopyMemory tmpByte(0), arrPCXbyte(0), UBound(arrPCXbyte) + 1
End If
DeleteDC tmpDC
DeleteObject tmpBitmap
With tmpPCX
.Manufacturer = 10
.Version = 5
.Encoding = 0
.BytesPP = 8
.Planes = 3
.xMin = 1
.yMin = 1
.w = inW
.h = inH
.hDPI = 96
.vDPI = 96
.Reserved = 0
.PaletteType = 0
.BytesPerLine = PCXw
End With
FormPCX24BPPbytes tmpByte, arrByte, PCXw, inH
'If compression is wanted, unblock
'CompressPCX arrByte, inScanW
mHandle = FreeFile
Open inFileSpec For Binary Access Read Write As mHandle
Put #mHandle, , tmpPCX
Put #mHandle, , arrByte
Close #mHandle
Erase arrPCXbyte
Erase tmpByte
SavePCX = True
End Function
Private Sub FormPCX24BPPbytes(inArrSrc() As Byte, inArrDest() As Byte, inScanW As Long, inH As Long)
Dim x As Long, y As Long
Dim nStartPos As Long
Dim h As Long
Dim arrBGR() As BGRcolorspace
ReDim arrBGR(inScanW * inH)
CopyMemory arrBGR(0), inArrSrc(0), (inScanW * inH * 3)
ReDim inArrDest(inScanW * inH * 3 - 1)
For y = 0 To inH - 1
For x = 0 To inScanW - 1
h = inH - y - 1
nStartPos = h * inScanW * 3 + x
With arrBGR((x + (inH - 1 - y) * inScanW))
inArrDest(nStartPos) = .r
inArrDest(nStartPos + inScanW) = .G
inArrDest(nStartPos + inScanW * 2) = .B
End With
Next x
Next y
End Sub
Edited: I've added two comment lines inside Function SavePCX, as below:
'If compression is wanted, unblock
'CompressPCX arrByte, inScanW
Last edited by petersen; Dec 31st, 2010 at 06:15 PM.
I must admit I didn't recall seeing the FormPCX24BPPbytes function in the other thread. My bad...
That function is doing it all. It is realigning BGR to RGB and also grouping the Reds, Greens & Blues as would be needed.
Edited: I also recognize that function (at least most of it). If you are using his code for reading pcx's, he had a major logic error. The code would read the entire pcx file from byte 128 to end of file, and decode along the way. That in itself isn't the problem. For 8bpp images, the last 769 bytes are palette info and not encoded data and there may have been other non-data stuff between the last encoded pixel and end of file. However, he skipped errors by only reading the appropriate n-bytes from the decoded data. I just thought it was a waste of time decoding stuff that would never be used. Just FYI.
Last edited by LaVolpe; Dec 31st, 2010 at 06:21 PM.
Insomnia is just a byproduct of, "It can't be done"
(1) No, I have not the least problem of loading PCX all along -- whatever IrfanView can load, mine can load.
(2) A quick search through the project shows that SavePCX the only place calling that function in entire project.
Edited: I keep IrfanView because it is handy for testing something sometimes. Before these couple days I'd never realized it is that strong in PCX (it has many weak areas, e.g. it cannot properly display the 2 animated GIF that I sent you).
Last edited by petersen; Dec 31st, 2010 at 07:14 PM.
I think I have my 24/32 bpp compressor working correctly. Want to give it some more tests & will post it tomorrow when I feel comfortable.
Regarding 32bpp alpha pcx files...
Interesting. The image on left is MS PhotoEditor. I didn't expect it to load correctly simply because it can't load uncompressed images either & 32bpp isn't exactly the norm for pcx. But the 2nd viewer is ImageMagick and it loads just fine. Curious if it'll load on any of your viewers. I think I will keep it as an alternative for saving to pcx...
Insomnia is just a byproduct of, "It can't be done"
Then that may very well change my mind. If some of the common viewers won't display it, then what's the point of offering it other than for personal reasons? Looks like the code will stay in, just turned off. As I mentioned earlier, adding 32bpp ability didn't add any real code overhead.
Insomnia is just a byproduct of, "It can't be done"
JPEG 2K is a newer format and many viewers may not know how to fully render them. But PCX, on the other hand, is a dying format and doubt any effort will be made to extend its capabilities. If pcx had a better compression algo, it might still be in wide use today. But it really can't compete, as-is, with formats that came later: GIF, JPG, PNG
Insomnia is just a byproduct of, "It can't be done"
Here's my 24bit compression routine. You may find it compresses very well, pcx-speaking
Edited: I've also tweaked the code in post #4 which can shave off 1 byte per scanline.
Code:
' rawStream() is passed source image data as 24bit as 2D array. Change code below for 1D arrays
' imgWidth & imgHeight are passed source image size
' bOut will contain the pcx data & you fill in the 128-byte header
' special note: the header's BytesPerLine value is calculated as: imgWidth + (imgWidth And 1)
Dim bOut() As Byte, bEncoded() As Byte
Dim ePtr As Long, X As Long, Y As Long, bPrev As Byte
Dim srcPtr As Long, lChannel As Long, lCount As Long
'///// uncompressed routine
' size array exactly as needed: 128 header, image data
ReDim bOut(0 To 127& + (imgWidth + (imgWidth And 1&)) * imgHeight * 3&)
ePtr = 128&
For Y = imgHeight - 1& To 0& Step -1&
For lChannel = 0& To 2&
' pcx is rgb but source is bgr. Ensure X refers to correct color channel
If (lChannel And 1&) Then X = lChannel Else X = (lChannel Xor 2&)
For X = X To lScanWidth - 1& Step 3&
bOut(ePtr) = rawStream(X, Y)
ePtr = ePtr + 1&
Next
ePtr = ePtr + (imgWidth And 1&)
Next
Next
'///// compressed routine
ReDim bEncoded(0 To (imgWidth + (imgWidth And 1&) * 2&) * 3&)
ReDim bOut(0 To 127)
For Y = imgHeight - 1& To 0& Step -1&
ePtr = 0&
bPrev = rawStream(2, Y) ' set to first red bit
lCount = 1&: X = 3&
For lChannel = 0& To 2&
' pcx is rgb but source is bgr. Ensure X refers to correct color channel
If (lChannel And 1&) Then srcPtr = lChannel Else srcPtr = (lChannel Xor 2&)
For srcPtr = srcPtr + X To lScanWidth - 1& Step 3&
If rawStream(srcPtr, Y) = bPrev Then
If lCount > 62& Then
bEncoded(ePtr) = &HFF: bEncoded(ePtr + 1&) = bPrev
lCount = lCount - 63&
ePtr = ePtr + 2&
End If
lCount = lCount + 1&
Else
If lCount = 1& Then
If (bPrev And &HC0) = &HC0 Then
bEncoded(ePtr) = &HC1: bEncoded(ePtr + 1&) = bPrev
ePtr = ePtr + 2&
Else
bEncoded(ePtr) = bPrev: ePtr = ePtr + 1&
End If
Else
If lCount > 63& Then
bEncoded(ePtr) = &HFF: bEncoded(ePtr + 1&) = bPrev
If lCount - 63& Then
bEncoded(ePtr + 2&) = (lCount - 63&) Or &HC0: bEncoded(ePtr + 3&) = bPrev
ePtr = ePtr + 2&
End If
Else
bEncoded(ePtr) = lCount Or &HC0: bEncoded(ePtr + 1&) = bPrev
End If
ePtr = ePtr + 2&
End If
lCount = 1&
bPrev = rawStream(srcPtr, Y)
End If
Next
lCount = lCount + (imgWidth And 1&)
X = 0&
Next
' clean up scan line
If lCount = 1& Then
If (bPrev And &HC0) = &HC0 Then
bEncoded(ePtr) = &HC1: bEncoded(ePtr + 1&) = bPrev
ePtr = ePtr + 2&
Else
bEncoded(ePtr) = bPrev: ePtr = ePtr + 1&
End If
Else
bEncoded(ePtr + 1&) = bPrev
If lCount > 63& Then
bEncoded(ePtr) = &HFF
bEncoded(ePtr + 2&) = &HC0 Or (lCount - 63&)
bEncoded(ePtr + 3&) = bPrev
ePtr = ePtr + 4&
Else
bEncoded(ePtr) = lCount Or &HC0
ePtr = ePtr + 2&
End If
End If
' transfer data to return array
lCount = UBound(bOut) + 1&
ReDim Preserve bOut(0 To lCount + ePtr - 1&)
CopyMemory bOut(lCount), bEncoded(0), ePtr
Next
Edited: Optimized compression as much as I can. Compared to MS PhotoEditor, compression produces identical byte-sized images
Last edited by LaVolpe; Jan 1st, 2011 at 03:36 AM.
Insomnia is just a byproduct of, "It can't be done"
Without a ready-to-run sample project, I wouldn't be able to test saving some images and then load them to PhotoShop7 to see whether your subroutine works. Because of this, I will stay with my single dimension array to use and do some examination later on (i) as my existing subroutine is 100% okay with IrfanView and basically all ok with FreeImage, the only problem is with PhotoShop7, I don't want to overhaul my code to a 2-dimension array just for testing your code and (ii) since pcx users are extremely rare, there is no immediate urgency for me.
By changing a typo of "=" to ">" in a single place, I have already resolved the problem for which this thread is created. The only thing outstanding now is about PhotoShop7 only -- If I am to test your ready-to-run project, it would be to confirm whether it is PhotoShop7's inherent showtcoming, or due to my code still.
Ok, attached is a 256x256 compressed 24bpp pcx & the compression is quick too. If it displays properly in your viewers, again a testimate to routine accuracy. I know you aren't expecting me to modify the routine for you
The routines are now in my Alpha Image Control and I don't feel like taking them out and adding them to a project of its own. The file should be good enough for proof of concept/viability. FYI. Alpha Image Control not updated yet on this site; coming this weekend
Here's one even and one odd width-sized pcx each.
Edited: I do believe that no other routines can compress 24bit better than the algo I offered in previous post. Optimizing it to RLE across color channels was the key. The only possible way to compress even further would be: a) modify colors of image (cheating), or 2) run RLE across scanlines. Since cross-scanline RLE is against pcx specs, I won't entertain that idea.
Last edited by LaVolpe; Jan 1st, 2011 at 02:42 AM.
Insomnia is just a byproduct of, "It can't be done"
...The only thing outstanding now is about PhotoShop7 only -- If I am to test your ready-to-run project, it would be to confirm whether it is PhotoShop7's inherent showtcoming, or due to my code still.
Well, if you want to post that source image (not your pcx, but the image you created pcx from), I'll convert it to pcx using my routines (same as posted earlier). You can bump it up against PhotoShop7. At least that should resolve that last issue I'd think... But I'd lean towards your code. As I mentioned earlier, if pcx was formatted to specifications, any reputable reader should be able to display it correctly.
Last edited by LaVolpe; Jan 1st, 2011 at 03:48 AM.
Insomnia is just a byproduct of, "It can't be done"
PhotoShop is also okay now. This means the subroutine has been well tested, with actual data saved as 8-BPP and/or 24-BPP of different sizes (from small to big), and the saved 8-BPP and 24-BPP files loaded and displayed on AZ Paint Pro, IrfanView, FreeImage and PhotoShop all alright.
Case thus satisfactorily concluded.
Last edited by petersen; Jan 1st, 2011 at 04:35 PM.
Thanks. I've meanwhile satisfactorily completed my only outstanding item (i.e. PhotoShop item), as said in above. However, if Save PCX function is available in your project, I would still like to do a couple of test runs for you - just in case I find anything.
Continue loading. I thought I removed that reference. It was something I was playing with.
I'll quickly update the project so others don't experience same issue. Done.
Insomnia is just a byproduct of, "It can't be done"
That's a new one for me. Immediately, I have no clue as to the reason and have never seen it before with this project. Honestly I don't even know how it can error. I'd say the message is totally correct: Unexpected Error
You can try this as a temp solution, or just rem it out. I'll take a look and see if I can replicate it.
Code:
If UserControl.MouseIcon <> 0 Then .WriteProperty "MouseIco", UserControl.MouseIcon
Insomnia is just a byproduct of, "It can't be done"
I wouldn't open it to IE browser as I got occassional crashes too while uncompiled. It has to something to do with GDI+. Don't make it the startup project, make any of the others in your group project the startup project.
You're already familiar with this control. Have you had issues before like this? Very little of the control changed, basically added a new class and approrpiate lines of code to initialize the class and call one of its only 3 functions.
Also, more out of curiosity. What O/S is that?
Last edited by LaVolpe; Jan 1st, 2011 at 09:29 PM.
Insomnia is just a byproduct of, "It can't be done"
I was wondering what is that. Because it carries the word "AlphaImageCtrl..", so I thought it must have something to do with your Image Control project. (Otherwise I wouldn't have that portion included in the screenshot).
(2) After the crash, I tried to clear the temp folder. I found that file locked (I don't know what the file is):
~DF5C92.bmp (I thnik we should discard this point, because I cannot relate anything to AlphaImageCtrl specifically)
(3) For your info, when I start your project: (a) No firewall, (b) No antivirus and (c) No any other program running (purposely so).
(4) I am on Vista, Home Premium.
(5) Actually I wouldn't say I am familiar with the project. Yes I test run the project a couple times before, but limited to loading some images to the Form of the Added Project (e.g. when I saw the image displayed was not right, e.g. the animated GIF cases I posted, I jumped out from there).
Last edited by petersen; Jan 1st, 2011 at 09:59 PM.
I get the exact same screenshot from IE. It shows that when the control is the startup project. IE acts as sort of the default test enviornment for active-x controls unless you choose to start with a different project.
P.S. While tidying up some pcx-related comments, I found a bug in the project. The bug prevented palettizing to 8 bit, so no 256 color pcx files would be created. I since fixed that problem & updated the project one more time. The error handling avoided any nasties, but simply aborted the palettizing routine. Apoligize for the inconvenience.
The tmp files are created every time we run uncompiled projects. They are normally deleted if project doesn't crash. I go in an clean them up a few times a year.
Insomnia is just a byproduct of, "It can't be done"
I run ".vbg" (group project) this time, not the control. (whereas before, I run the control, then add New Project, it was on the Form of the New Project where I tested loading some test images -- no group project created).
Last edited by petersen; Jan 1st, 2011 at 10:31 PM.
Did you set your other project as the startup? If the project launches IE, then the Alpha Image Control is the startup project and you don't want that.
1) Create a new project, not the Alpha Image Control project
2) With VB's IDE menu: File | Add Project
3) Navigate to and select the Alpha Image Control project
4) Now add a control to the form in your new project, add an image and run the project
You should have no errors
But when you load the Alpha Image Control project first, it will be the startup. When adding any other projects, the Alpha Image Control project is still the startup. You'll need to right click on the other project in the treeview and select menu: Set as Start Up
I'm bouncing off XP and loading Vista to see if I have any issues. If I do, I'll post back here shortly. Don't expect any.
Edited: On Vista now & no problems at all.
Last edited by LaVolpe; Jan 2nd, 2011 at 12:19 AM.
Insomnia is just a byproduct of, "It can't be done"
I loaded an image not exceeding 256 colors. Then save it to PCX, using the syntax you gave. No error this time and it can be loaded okay. However, the file size is 1,919K 24-BPP (whereas mine is 763K, 8-BPP automatically on Save). Not sure it is the case.
Last edited by petersen; Jan 2nd, 2011 at 02:33 AM.
Can you post that image. I'd appreciate it.
Only 1 of 3 things happened.
1) My palettizer detected alpha used & if so, aborts palettizing
2) My palettizer detected more than 256 colors
3) I have another hidden bug
Edited. Forget about it if it was a GIF source. Shortcut taken that I shouldn't have taken.
In the mean time, here's how you can turn it off so you can continue testing.
Routine: cFunctionsPCX.SaveToStream
Rem out these three lines
1) bAlpha = HasTransparency(Handle, ImageType, tBMP.PixelFormat, False)
2,3) If bAlpha = False Then ... and also rem out its End If statement
Last edited by LaVolpe; Jan 2nd, 2011 at 01:14 AM.
Insomnia is just a byproduct of, "It can't be done"