📄 jvgraph.pas
字号:
{-----------------------------------------------------------------------------
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: JvGraph.PAS, released on 2002-07-04.
The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev
Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev
Copyright (c) 2001,2002 SGB Software
All Rights Reserved.
Last Modified: 2002-07-04
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:
-----------------------------------------------------------------------------}
{$I JVCL.INC}
unit JvGraph;
interface
uses
{$IFDEF WIN32}
Windows,
{$ELSE}
WinTypes, WinProcs,
{$ENDIF}
SysUtils, Classes, Graphics,
JvVCLUtils;
type
{$IFNDEF COMPILER3_UP}
TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf24bit);
{$ENDIF}
TMappingMethod = (mmHistogram, mmQuantize, mmTrunc784, mmTrunc666,
mmTripel, mmGrayscale);
function GetBitmapPixelFormat(Bitmap: TBitmap): TPixelFormat;
function GetPaletteBitmapFormat(Bitmap: TBitmap): TPixelFormat;
procedure SetBitmapPixelFormat(Bitmap: TBitmap; PixelFormat: TPixelFormat;
Method: TMappingMethod);
function BitmapToMemoryStream(Bitmap: TBitmap; PixelFormat: TPixelFormat;
Method: TMappingMethod): TMemoryStream;
procedure GrayscaleBitmap(Bitmap: TBitmap);
function BitmapToMemory(Bitmap: TBitmap; Colors: Integer): TStream;
procedure SaveBitmapToFile(const Filename: string; Bitmap: TBitmap;
Colors: Integer);
function ScreenPixelFormat: TPixelFormat;
function ScreenColorCount: Integer;
procedure TileImage(Canvas: TCanvas; Rect: TRect; Image: TGraphic);
function ZoomImage(ImageW, ImageH, MaxW, MaxH: Integer; Stretch: Boolean): TPoint;
var
DefaultMappingMethod: TMappingMethod = mmHistogram;
type
TJvGradient = class(TPersistent)
private
FStartColor: TColor;
FEndColor: TColor;
FDirection: TFillDirection;
FStepCount: Byte;
FVisible: Boolean;
FOnChange: TNotifyEvent;
procedure SetStartColor(Value: TColor);
procedure SetEndColor(Value: TColor);
procedure SetDirection(Value: TFillDirection);
procedure SetStepCount(Value: Byte);
procedure SetVisible(Value: Boolean);
protected
procedure Changed; dynamic;
public
constructor Create;
procedure Assign(Source: TPersistent); override;
procedure Draw(Canvas: TCanvas; Rect: TRect);
published
property Direction: TFillDirection read FDirection write SetDirection default fdTopToBottom;
property EndColor: TColor read FEndColor write SetEndColor default clGray;
property StartColor: TColor read FStartColor write SetStartColor default clSilver;
property StepCount: Byte read FStepCount write SetStepCount default 64;
property Visible: Boolean read FVisible write SetVisible default False;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
implementation
{$R-}
uses
Consts;
// (rom) moved here to make JvMaxMin obsolete
function MaxFloat(const Values: array of Extended): Extended;
var
I: Cardinal;
begin
Result := Values[Low(Values)];
for I := Low(Values)+1 to High(Values) do
if Values[I] > Result then
Result := Values[I];
end;
procedure InvalidBitmap; near;
begin
raise EInvalidGraphic.Create(ResStr(SInvalidBitmap));
end;
type
PRGBPalette = ^TRGBPalette;
TRGBPalette = array [Byte] of TRGBQuad;
function WidthBytes(I: Longint): Longint;
begin
Result := ((I + 31) div 32) * 4;
end;
function PixelFormatToColors(PixelFormat: TPixelFormat): Integer;
begin
case PixelFormat of
pf1bit:
Result := 2;
pf4bit:
Result := 16;
pf8bit:
Result := 256;
else
Result := 0;
end;
end;
function ScreenPixelFormat: TPixelFormat;
var
DC: HDC;
begin
DC := GetDC(0);
try
case (GetDeviceCaps(DC, PLANES) * GetDeviceCaps(DC, BITSPIXEL)) of
1:
Result := pf1bit;
4:
Result := pf4bit;
8:
Result := pf8bit;
24:
Result := pf24bit;
else
Result := pfDevice;
end;
finally
ReleaseDC(0, DC);
end;
end;
function ScreenColorCount: Integer;
begin
Result := PixelFormatToColors(ScreenPixelFormat);
end;
{ Quantizing }
{ Quantizing procedures based on free C source code written by
Joe C. Oliphant, CompuServe 71742, 1451, joe_oliphant@csufresno.edu }
const
MAX_COLORS = 4096;
type
PQColor = ^TQColor;
TQColor = record
RGB: array [0..2] of Byte;
NewColorIndex: Byte;
Count: Longint;
PNext: PQColor;
end;
PQColorArray = ^TQColorArray;
TQColorArray = array [0..MAX_COLORS - 1] of TQColor;
PQColorList = ^TQColorList;
TQColorList = array [0..MaxListSize - 1] of PQColor;
PNewColor = ^TNewColor;
TNewColor = record
RGBMin, RGBWidth: array [0..2] of Byte;
NumEntries: Longint;
Count: Longint;
QuantizedColors: PQColor;
end;
PNewColorArray = ^TNewColorArray;
TNewColorArray = array[Byte] of TNewColor;
procedure PInsert(ColorList: PQColorList; Number: Integer;
SortRGBAxis: Integer);
var
Q1, Q2: PQColor;
I, J: Integer;
Temp: PQColor;
begin
for I := 1 to Number - 1 do
begin
Temp := ColorList^[I];
J := I - 1;
while J >= 0 do
begin
Q1 := Temp;
Q2 := ColorList^[J];
if Q1^.RGB[SortRGBAxis] - Q2^.RGB[SortRGBAxis] > 0 then
Break;
ColorList^[J + 1] := ColorList^[J];
Dec(J);
end;
ColorList^[J + 1] := Temp;
end;
end;
procedure PSort(ColorList: PQColorList; Number: Integer;
SortRGBAxis: Integer);
var
Q1, Q2: PQColor;
I, J, N, Nr: Integer;
Temp, Part: PQColor;
begin
if Number < 8 then
begin
PInsert(ColorList, Number, SortRGBAxis);
Exit;
end;
Part := ColorList^[Number div 2];
I := -1;
J := Number;
repeat
repeat
Inc(I);
Q1 := ColorList^[I];
Q2 := Part;
N := Q1^.RGB[SortRGBAxis] - Q2^.RGB[SortRGBAxis];
until N >= 0;
repeat
Dec(J);
Q1 := ColorList^[J];
Q2 := Part;
N := Q1^.RGB[SortRGBAxis] - Q2^.RGB[SortRGBAxis];
until N <= 0;
if I >= J then
Break;
Temp := ColorList^[I];
ColorList^[I] := ColorList^[J];
ColorList^[J] := Temp;
until False;
Nr := Number - I;
if I < Number div 2 then
begin
PSort(ColorList, I, SortRGBAxis);
PSort(PQColorList(@ColorList^[I]), Nr, SortRGBAxis);
end
else
begin
PSort(PQColorList(@ColorList^[I]), Nr, SortRGBAxis);
PSort(ColorList, I, SortRGBAxis);
end;
end;
function DivideMap(NewColorSubdiv: PNewColorArray; ColorMapSize: Integer;
var NewColormapSize: Integer; lpStr: Pointer): Integer;
var
I, J: {$IFDEF WIN32} Integer {$ELSE}Cardinal {$ENDIF};
MaxSize, Index: Integer;
NumEntries, MinColor,
MaxColor: {$IFDEF WIN32} Integer {$ELSE} Cardinal {$ENDIF};
Sum, Count: Longint;
QuantizedColor: PQColor;
SortArray: PQColorList;
SortRGBAxis: Integer;
begin
Index := 0;
SortRGBAxis := 0;
while ColorMapSize > NewColormapSize do
begin
MaxSize := -1;
for I := 0 to NewColormapSize - 1 do
begin
for J := 0 to 2 do
begin
if (NewColorSubdiv^[I].RGBwidth[J] > MaxSize) and
(NewColorSubdiv^[I].NumEntries > 1) then
begin
MaxSize := NewColorSubdiv^[I].RGBwidth[J];
Index := I;
SortRGBAxis := J;
end;
end;
end;
if MaxSize = -1 then
begin
Result := 1;
Exit;
end;
SortArray := PQColorList(lpStr);
J := 0;
QuantizedColor := NewColorSubdiv^[Index].QuantizedColors;
while (J < NewColorSubdiv^[Index].NumEntries) and
(QuantizedColor <> nil) do
begin
SortArray^[J] := QuantizedColor;
Inc(J);
QuantizedColor := QuantizedColor^.pnext;
end;
PSort(SortArray, NewColorSubdiv^[Index].NumEntries, SortRGBAxis);
for J := 0 to NewColorSubdiv^[Index].NumEntries - 2 do
SortArray^[J]^.pnext := SortArray^[J + 1];
SortArray^[NewColorSubdiv^[Index].NumEntries - 1]^.pnext := nil;
NewColorSubdiv^[Index].QuantizedColors := SortArray^[0];
QuantizedColor := SortArray^[0];
Sum := NewColorSubdiv^[Index].Count div 2 - QuantizedColor^.Count;
NumEntries := 1;
Count := QuantizedColor^.Count;
Dec(Sum, QuantizedColor^.pnext^.Count);
while (Sum >= 0) and (QuantizedColor^.pnext <> nil) and
(QuantizedColor^.pnext^.pnext <> nil) do
begin
QuantizedColor := QuantizedColor^.pnext;
Inc(NumEntries);
Inc(Count, QuantizedColor^.Count);
Dec(Sum, QuantizedColor^.pnext^.Count);
end;
MaxColor := (QuantizedColor^.RGB[SortRGBAxis]) shl 4;
MinColor := (QuantizedColor^.pnext^.RGB[SortRGBAxis]) shl 4;
NewColorSubdiv^[NewColormapSize].QuantizedColors := QuantizedColor^.pnext;
QuantizedColor^.pnext := nil;
NewColorSubdiv^[NewColormapSize].Count := Count;
Dec(NewColorSubdiv^[Index].Count, Count);
NewColorSubdiv^[NewColormapSize].NumEntries :=
NewColorSubdiv^[Index].NumEntries - NumEntries;
NewColorSubdiv^[Index].NumEntries := NumEntries;
for J := 0 to 2 do
begin
NewColorSubdiv^[NewColormapSize].RGBmin[J] :=
NewColorSubdiv^[Index].RGBmin[J];
NewColorSubdiv^[NewColormapSize].RGBwidth[J] :=
NewColorSubdiv^[Index].RGBwidth[J];
end;
NewColorSubdiv^[NewColormapSize].RGBwidth[SortRGBAxis] :=
NewColorSubdiv^[NewColormapSize].RGBmin[SortRGBAxis] +
NewColorSubdiv^[NewColormapSize].RGBwidth[SortRGBAxis] -
MinColor;
NewColorSubdiv^[NewColormapSize].RGBmin[SortRGBAxis] := MinColor;
NewColorSubdiv^[Index].RGBwidth[SortRGBAxis] :=
MaxColor - NewColorSubdiv^[Index].RGBmin[SortRGBAxis];
Inc(NewColormapSize);
end;
Result := 1;
end;
function Quantize(const bmp: TBitmapInfoHeader; gptr, Data8: Pointer;
var ColorCount: Integer; var OutputColormap: TRGBPalette): Integer;
type
PWord = ^Word;
var
P: PByteArray;
LineBuffer, Data: Pointer;
LineWidth: Longint;
TmpLineWidth, NewLineWidth: Longint;
I, J: Longint;
Index: Word;
NewColormapSize, NumOfEntries: Integer;
Mems: Longint;
cRed, cGreen, cBlue: Longint;
lpStr, Temp, Tmp: Pointer;
NewColorSubdiv: PNewColorArray;
ColorArrayEntries: PQColorArray;
QuantizedColor: PQColor;
begin
LineWidth := WidthBytes(Longint(bmp.biWidth) * bmp.biBitCount);
Mems := (Longint(SizeOf(TQColor)) * (MAX_COLORS)) +
(Longint(SizeOf(TNewColor)) * 256) + LineWidth +
(Longint(sizeof(PQCOLOR)) * (MAX_COLORS));
lpStr := AllocMemo(Mems);
try
Temp := AllocMemo(Longint(bmp.biWidth) * Longint(bmp.biHeight) *
SizeOf(Word));
try
ColorArrayEntries := PQColorArray(lpStr);
NewColorSubdiv := PNewColorArray(HugeOffset(lpStr,
Longint(sizeof(TQColor)) * (MAX_COLORS)));
LineBuffer := HugeOffset(lpStr, (Longint(sizeof(TQColor)) * (MAX_COLORS)) +
(Longint(sizeof(TNewColor)) * 256));
for I := 0 to MAX_COLORS - 1 do
begin
ColorArrayEntries^[I].RGB[0] := I shr 8;
ColorArrayEntries^[I].RGB[1] := (I shr 4) and $0F;
ColorArrayEntries^[I].RGB[2] := I and $0F;
ColorArrayEntries^[I].Count := 0;
end;
Tmp := Temp;
for I := 0 to bmp.biHeight - 1 do
begin
HMemCpy(LineBuffer, HugeOffset(gptr, (bmp.biHeight - 1 - I) *
LineWidth), LineWidth);
P := LineBuffer;
for J := 0 to bmp.biWidth - 1 do
begin
Index := (Longint(P^[2] and $F0) shl 4) +
Longint(P^[1] and $F0) + (Longint(P^[0] and $F0) shr 4);
Inc(ColorArrayEntries^[Index].Count);
P := HugeOffset(P, 3);
PWord(Tmp)^ := Index;
Tmp := HugeOffset(Tmp, 2);
end;
end;
for I := 0 to 255 do
begin
NewColorSubdiv^[I].QuantizedColors := nil;
NewColorSubdiv^[I].Count := 0;
NewColorSubdiv^[I].NumEntries := 0;
for J := 0 to 2 do
begin
NewColorSubdiv^[I].RGBmin[J] := 0;
NewColorSubdiv^[I].RGBwidth[J] := 255;
end;
end;
I := 0;
while I < MAX_COLORS do
begin
if ColorArrayEntries^[I].Count > 0 then
Break;
Inc(I);
end;
QuantizedColor := @ColorArrayEntries^[I];
NewColorSubdiv^[0].QuantizedColors := @ColorArrayEntries^[I];
NumOfEntries := 1;
Inc(I);
while I < MAX_COLORS do
begin
if ColorArrayEntries^[I].Count > 0 then
begin
QuantizedColor^.pnext := @ColorArrayEntries^[I];
QuantizedColor := @ColorArrayEntries^[I];
Inc(NumOfEntries);
end;
Inc(I);
end;
QuantizedColor^.pnext := nil;
NewColorSubdiv^[0].NumEntries := NumOfEntries;
NewColorSubdiv^[0].Count := Longint(bmp.biWidth) * Longint(bmp.biHeight);
NewColormapSize := 1;
DivideMap(NewColorSubdiv, ColorCount, NewColormapSize,
HugeOffset(lpStr, Longint(SizeOf(TQColor)) * (MAX_COLORS) +
Longint(SizeOf(TNewColor)) * 256 + LineWidth));
if NewColormapSize < ColorCount then
begin
for I := NewColormapSize to ColorCount - 1 do
FillChar(OutputColormap[I], SizeOf(TRGBQuad), 0);
end;
for I := 0 to NewColormapSize - 1 do
begin
J := NewColorSubdiv^[I].NumEntries;
if J > 0 then
begin
QuantizedColor := NewColorSubdiv^[I].QuantizedColors;
cRed := 0;
cGreen := 0;
cBlue := 0;
while QuantizedColor <> nil do
begin
QuantizedColor^.NewColorIndex := I;
Inc(cRed, QuantizedColor^.RGB[0]);
Inc(cGreen, QuantizedColor^.RGB[1]);
Inc(cBlue, QuantizedColor^.RGB[2]);
QuantizedColor := QuantizedColor^.pnext;
end;
with OutputColormap[I] do
begin
rgbRed := (Longint(cRed shl 4) or $0F) div J;
rgbGreen := (Longint(cGreen shl 4) or $0F) div J;
rgbBlue := (Longint(cBlue shl 4) or $0F) div J;
rgbReserved := 0;
if (rgbRed <= $10) and (rgbGreen <= $10) and (rgbBlue <= $10) then
FillChar(OutputColormap[I], SizeOf(TRGBQuad), 0); { clBlack }
end;
end;
end;
TmpLineWidth := Longint(bmp.biWidth) * SizeOf(Word);
NewLineWidth := WidthBytes(Longint(bmp.biWidth) * 8);
FillChar(Data8^, NewLineWidth * bmp.biHeight, #0);
for I := 0 to bmp.biHeight - 1 do
begin
LineBuffer := HugeOffset(Temp, (bmp.biHeight - 1 - I) * TmpLineWidth);
Data := HugeOffset(Data8, I * NewLineWidth);
for J := 0 to bmp.biWidth - 1 do
begin
PByte(Data)^ := ColorArrayEntries^[PWord(LineBuffer)^].NewColorIndex;
LineBuffer := HugeOffset(LineBuffer, 2);
Data := HugeOffset(Data, 1);
end;
end;
finally
FreeMemo(Temp);
end;
finally
FreeMemo(lpStr);
end;
ColorCount := NewColormapSize;
Result := 0;
end;
{
Procedures to truncate to lower bits-per-pixel, grayscale, tripel and
histogram conversion based on freeware C source code of GBM package by
Andy Key (nyangau@interalpha.co.uk). The home page of GBM author is
at http://www.interalpha.net/customer/nyangau/.
}
{ Truncate to lower bits per pixel }
type
TTruncLine = procedure(Src, Dest: Pointer; CX: Integer);
{ For 6Rx6Gx6B, 7Rx8Gx4B palettes etc. }
const
Scale04: array [0..3] of Byte = (0, 85, 170, 255);
Scale06: array [0..5] of Byte = (0, 51, 102, 153, 204, 255);
Scale07: array [0..6] of Byte = (0, 43, 85, 128, 170, 213, 255);
Scale08: array [0..7] of Byte = (0, 36, 73, 109, 146, 182, 219, 255);
{ For 6Rx6Gx6B, 7Rx8Gx4B palettes etc. }
var
TruncIndex04: array [Byte] of Byte;
TruncIndex06: array [Byte] of Byte;
TruncIndex07: array [Byte] of Byte;
TruncIndex08: array [Byte] of Byte;
{ These functions initialises this module }
procedure InitTruncTables;
function NearestIndex(Value: Byte; const Bytes: array of Byte): Byte;
var
B, I: Byte;
Diff, DiffMin: Word;
begin
Result := 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -