Componentes.Terceros.jvcl/official/3.36/run/JvPcx.pas
2009-02-27 12:23:32 +00:00

514 lines
15 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvPcx.PAS, released on 2001-02-28.
The Initial Developer of the Original Code is Sébastien Buysse [sbuysse att buypin dott com]
Portions created by Sébastien Buysse are Copyright (C) 2001 Sébastien Buysse.
All Rights Reserved.
Contributor(s): Michael Beck [mbeck att bigfoot dott com].
Andreas Hausladen [Andreas dott Hausladen att gmx dott de] (complete rewrite)
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.sourceforge.net
Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvPcx.pas 11400 2007-06-28 21:24:06Z ahuser $
unit JvPcx;
{$I jvcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF MSWINDOWS}
{$IFDEF UNIX}
QWindows,
{$ENDIF UNIX}
Graphics, Controls, Forms,
SysUtils, Classes,
JvTypes, JvJCLUtils;
type
EPcxError = class(EJVCLException);
TJvPcx = class(TBitmap)
public
procedure LoadFromResourceName(Instance: THandle; const ResName: string; ResType: PChar);
{$IFDEF MSWINDOWS}
procedure LoadFromResourceID(Instance: THandle; ResID: Integer; ResType: PChar);
{$ENDIF MSWINDOWS}
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToStream(Stream: TStream); override;
end;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/branches/JVCL3_36_PREPARATION/run/JvPcx.pas $';
Revision: '$Revision: 11400 $';
Date: '$Date: 2007-06-28 23:24:06 +0200 (jeu., 28 juin 2007) $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
uses
JvResources;
procedure TJvPcx.LoadFromResourceName(Instance: THandle;
const ResName: string; ResType: PChar);
var
Stream: TStream;
begin
Assign(nil); // fixes GDI resource leak
if ResType = RT_BITMAP then
inherited LoadFromResourceName(Instance, ResName)
else
begin
Stream := TResourceStream.Create(Instance, ResName, ResType);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
end;
{$IFDEF MSWINDOWS}
procedure TJvPcx.LoadFromResourceID(Instance: THandle; ResID: Integer;
ResType: PChar);
var
Stream: TStream;
begin
Assign(nil); // fixes GDI resource leak
if ResType = RT_BITMAP then
inherited LoadFromResourceID(Instance, ResID)
else
begin
Stream := TResourceStream.CreateFromID(Instance, ResID, ResType);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
end;
{$ENDIF MSWINDOWS}
type
PPcxPalette = ^TPcxPalette;
TPcxPalette = packed record
Red: Byte;
Green: Byte;
Blue: Byte;
end;
PPcxPaletteArray = ^TPcxPaletteArray;
TPcxPaletteArray = array [0..255] of TPcxPalette;
TPcxPalette256 = packed record
Id: Byte; // $0C
Items: array [0..255] of TPcxPalette;
end;
TPcxHeader = packed record
Id: Byte; // $0A
Version: Byte; // 5 = 3.0
Compressed: Boolean;
Bpp: Byte;
x0, y0: Word;
x1, y1: Word;
dpiX: Word;
dpiY: Word;
Palette16: array [0..15] of TPcxPalette;
Reserved1: Byte;
Planes: Byte;
BytesPerLine: Word;
PaletteType: Word; // 1: color or s/w 2: grayscaled
ScreenWidth: Word; // 0
ScreenHeight: Word; // 0
Reserved2: array [0..53] of Byte;
end;
procedure ReadPalette(Bitmap: TJvPcx; ColorNum: Integer; PcxPalette: PPcxPalette);
var
I: Integer;
P: PPcxPaletteArray;
RPal: TMaxLogPalette;
begin
P := PPcxPaletteArray(PcxPalette);
RPal.palVersion := $300;
RPal.palNumEntries := ColorNum;
for I := 0 to ColorNum - 1 do
begin
RPal.palPalEntry[I].peRed := P[I].Red;
RPal.palPalEntry[I].peGreen := P[I].Green;
RPal.palPalEntry[I].peBlue := P[I].Blue;
RPal.palPalEntry[I].peFlags := 0;
end;
Bitmap.Palette := CreatePalette(PLogPalette(@RPal)^);
Bitmap.PaletteModified := True;
end;
procedure WritePalette(Bitmap: TJvPcx; ColorNum: Integer; PcxPalette: PPcxPalette);
var
I: Integer;
P: PPcxPaletteArray;
RPal: array [0..256] of TPaletteEntry;
begin
P := PPcxPaletteArray(PcxPalette);
FillChar(P[0], ColorNum * SizeOf(TPcxPalette), 0);
if Bitmap.Palette <> 0 then
begin
GetPaletteEntries(Bitmap.Palette, 0, ColorNum, RPal);
for I := 0 to ColorNum - 1 do
begin
P[I].Red := RPal[I].peRed;
P[I].Green := RPal[I].peGreen;
P[I].Blue := RPal[I].peBlue;
end;
end;
end;
procedure TJvPcx.LoadFromStream(Stream: TStream);
var
Header: TPcxHeader;
BytesRead, BytesPerRasterLine: Integer;
Decompressed: TMemoryStream;
ByteLine: PByteArray;
Line: PJvRGBArray;
Palette256: TPcxPalette256;
Buffer: array [0..MaxPixelCount] of Byte;
Buffer2, Buffer3, Buffer4: PByteArray; // position in Buffer
B: Byte;
ByteNum, BitNum: Integer;
X, Y: Integer;
begin
Width := 0;
Height := 0;
Palette := 0;
IgnorePalette := False;
Monochrome := False;
BytesRead := Stream.Read(Header, SizeOf(Header));
// is it a valid header
if (BytesRead <> SizeOf(Header)) or (Header.Id <> $0A) or
(Header.BytesPerLine mod 2 = 1) then // BytesPerLine must be even
raise EPcxError.CreateRes(@RsEPcxInvalid);
// set pixel format before resizing the bitmap to reduce bitmap reallocations
case Header.Bpp of
1:
case Header.Planes of
1:
begin
PixelFormat := pf1bit;
Monochrome := True;
IgnorePalette := True;
end;
4:
PixelFormat := pf4bit; // VisualCLX: redirected const
else
raise EPcxError.CreateRes(@RsEPcxUnknownFormat);
end;
8:
case Header.Planes of
1:
PixelFormat := pf8bit;
3:
begin
PixelFormat := pf24bit; // VisualCLX: redirected const
IgnorePalette := True;
end;
else
raise EPcxError.CreateRes(@RsEPcxUnknownFormat);
end;
end;
Width := Header.x1 - Header.x0 + 1;
Height := Header.y1 - Header.y0 + 1;
if (Width = 0) or (Height = 0) then
Exit; // nothing to do
BytesPerRasterLine := Header.BytesPerLine * Header.Planes;
Decompressed := TMemoryStream.Create;
try
if (Header.Bpp = 8) and (Header.Planes = 1) then
// do not uncompress the appended (uncompressed) palette
Decompressed.CopyFrom(Stream, Stream.Size - Stream.Position - SizeOf(TPcxPalette256))
else
Decompressed.CopyFrom(Stream, Stream.Size - Stream.Position);
// decompress data stream
if Header.Compressed then
RleDecompress(Decompressed);
if (Header.Bpp = 8) and (Header.Planes = 1) then
// append the uncompressed palette
Decompressed.CopyFrom(Stream, SizeOf(TPcxPalette256));
// create palette (if necessary)
if (Header.Bpp = 1) and (Header.Planes = 4) then
begin
ReadPalette(Self, 16, @Header.Palette16[0]);
end
else
if (Header.Bpp = 8) and (Header.Planes = 1) then
begin
Decompressed.Seek(-SizeOf(TPcxPalette256), soFromEnd);
if Decompressed.Read(Palette256, SizeOf(TPcxPalette256)) <> SizeOf(TPcxPalette256) then
raise EPcxError.CreateRes(@RsEPcxPaletteProblem);
if Palette256.Id = $0C then
ReadPalette(Self, 256, @Palette256.Items[0])
else
raise EPcxError.CreateRes(@RsEPcxPaletteProblem);
end;
Decompressed.Position := 0;
// read data
for Y := 0 to Height - 1 do
begin
ByteLine := ScanLine[Y];
if Decompressed.Read(Buffer, BytesPerRasterLine) <> BytesPerRasterLine then
raise EPcxError.CreateRes(@RsEPcxUnknownFormat);
// write data to the ScanLine
if ((Header.Bpp = 1) and (Header.Planes = 1)) or // 1bit
((Header.Bpp = 8) and (Header.Planes = 1)) then // 8bit
// just copy the data
Move(Buffer[0], ByteLine[0], Header.BytesPerLine)
else
if (Header.Bpp = 8) and (Header.Planes = 3) then // 24bit
begin
Line := Pointer(ByteLine);
Buffer2 := @Buffer[Header.BytesPerLine];
Buffer3 := @Buffer[Header.BytesPerLine * 2];
for X := 0 to Width - 1 do
with Line[X] do
begin
rgbRed := Buffer[X];
rgbGreen := Buffer2[X];
rgbBlue := Buffer3[X];
end;
end
else
if (Header.Bpp = 1) and (Header.Planes = 4) then // 4bit
begin
Buffer2 := @Buffer[Header.BytesPerLine];
Buffer3 := @Buffer[Header.BytesPerLine * 2];
Buffer4 := @Buffer[Header.BytesPerLine * 3];
FillChar(ByteLine[0], BytesPerRasterLine, 0);
for X := 0 to Width - 1 do
begin
B := 0;
ByteNum := X div 8;
BitNum := 7 - (X mod 8);
if (Buffer[ByteNum] shr BitNum) and $1 <> 0 then
B := B or $01;
if (Buffer2[ByteNum] shr BitNum) and $1 <> 0 then
B := B or $02;
if (Buffer3[ByteNum] shr BitNum) and $1 <> 0 then
B := B or $04;
if (Buffer4[ByteNum] shr BitNum) and $1 <> 0 then
B := B or $08;
if X mod 2 = 0 then // BIG ENDIAN
B := B shl 4;
ByteLine[X div 2] := ByteLine[X div 2] or B;
end;
end;
end;
finally
Decompressed.Free;
end;
PaletteModified := True;
Changed(Self);
end;
procedure TJvPcx.SaveToStream(Stream: TStream);
var
CompressStream: TMemoryStream;
Header: TPcxHeader;
X, Y: Integer;
ByteLine: PByteArray;
Line: PJvRGBArray;
Buffer: array [0..MaxPixelCount] of Byte;
Buffer2, Buffer3, Buffer4: PByteArray; // position in Buffer
Palette256: TPcxPalette256;
BytesPerRasterLine: Integer;
B: Byte;
ByteNum, BitNum: Integer;
begin
if PixelFormat in [pfDevice, pfCustom, pf15bit, pf16bit] then
PixelFormat := pf24bit;
FillChar(Header, SizeOf(Header), 0);
Header.Id := $0A;
Header.Version := 5; // = 3.0
Header.Compressed := True;
Header.dpiX := 72;
Header.dpiY := 72;
Header.x1 := Width - 1;
Header.y1 := Height - 1;
Header.PaletteType := 1;
CompressStream := TMemoryStream.Create;
try
// complete header
case PixelFormat of
pf1bit:
begin
Header.Bpp := 1;
Header.Planes := 1;
Header.BytesPerLine := (Width + 7) div 8;
Header.Palette16[1].Red := 255;
Header.Palette16[1].Green := 255;
Header.Palette16[1].Blue := 255;
end;
pf4bit:
begin
Header.Bpp := 1;
Header.Planes := 4;
Header.BytesPerLine := (Width + 1) div 2;
end;
pf8bit:
begin
begin
Header.Bpp := 8;
Header.Planes := 1;
Header.BytesPerLine := Width;
end;
end;
pf24bit:
begin
Header.Bpp := 8;
Header.Planes := 3;
Header.BytesPerLine := Width;
end;
end;
// round BytesPerPixel to even
BytesPerRasterLine := Header.BytesPerLine; // save it
if Header.BytesPerLine mod 2 = 1 then
Inc(Header.BytesPerLine);
if (PixelFormat = pf8bit) or (PixelFormat = pf4bit) then
// copy first 16 palette entries into the header (also for pf8bit)
WritePalette(Self, 16, @Header.Palette16[0]);
// write header
Stream.Write(Header, SizeOf(Header));
// compress data
for Y := 0 to Height - 1 do
begin
ByteLine := ScanLine[Y];
case Header.Planes * Header.Bpp of // reduces VisualCLX IFDEFs
1, 8:
begin
if Header.BytesPerLine <> BytesPerRasterLine then
begin
Move(ByteLine[0], Buffer, BytesPerRasterLine);
Buffer[BytesPerRasterLine] := 0;
ByteLine := @Buffer[0];
end;
CompressStream.Write(ByteLine[0], Header.BytesPerLine);
end;
4:
begin
BytesPerRasterLine := Header.BytesPerLine * 4;
Buffer2 := @Buffer[Header.BytesPerLine];
Buffer3 := @Buffer[Header.BytesPerLine * 2];
Buffer4 := @Buffer[Header.BytesPerLine * 3];
FillChar(Buffer[0], BytesPerRasterLine, 0);
for X := 0 to Width - 1 do
begin
B := ByteLine[X div 2];
if X mod 2 = 0 then // BIG ENDIAN
B := B shr 4
else
B := B and $0F;
ByteNum := X div 8;
BitNum := 7 - (X mod 8);
if B and $01 <> 0 then
Buffer[ByteNum] := Buffer[ByteNum] or (1 shl BitNum);
if B and $02 <> 0 then
Buffer2[ByteNum] := Buffer2[ByteNum] or (1 shl BitNum);
if B and $04 <> 0 then
Buffer3[ByteNum] := Buffer3[ByteNum] or (1 shl BitNum);
if B and $08 <> 0 then
Buffer4[ByteNum] := Buffer4[ByteNum] or (1 shl BitNum);
end;
CompressStream.Write(Buffer, BytesPerRasterLine);
end;
24:
begin
Line := ScanLine[Y];
Buffer2 := @Buffer[Header.BytesPerLine];
Buffer3 := @Buffer[Header.BytesPerLine * 2];
for X := 0 to Width - 1 do
begin
with Line[X] do
begin
Buffer[X] := rgbRed;
Buffer2[X] := rgbGreen;
Buffer3[X] := rgbBlue;
end;
end;
CompressStream.Write(Buffer, Header.BytesPerLine * 3);
end;
end;
RleCompressTo(CompressStream, Stream);
CompressStream.Size := 0;
end;
// write palette
if PixelFormat = pf8bit then
begin
Palette256.Id := $0C;
WritePalette(Self, 256, @Palette256.Items[0]);
Stream.Write(Palette256, SizeOf(Palette256));
end;
finally
CompressStream.Free;
end;
end;
initialization
{$IFDEF UNITVERSIONING}
RegisterUnitVersion(HInstance, UnitVersioning);
{$ENDIF UNITVERSIONING}
{$IFDEF COMPILER7_UP}
GroupDescendentsWith(TJvPcx, TControl);
{$ENDIF COMPILER7_UP}
RegisterClass(TJvPcx);
TPicture.RegisterFileFormat(RsPcxExtension, RsPcxFilterName, TJvPcx);
finalization
TPicture.UnregisterGraphicClass(TJvPcx);
{$IFDEF UNITVERSIONING}
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.