lazarus/lcl/graphtype.pp
marc 84d4e52eae Add scanline support
git-svn-id: trunk@41855 -
2013-06-23 19:46:43 +00:00

1835 lines
56 KiB
ObjectPascal

{ $Id$ }
{
/***************************************************************************
graphtype.pp
------------
Graphic related platform independent types
and utility functions.
Initial Revision : Sat Feb 02 0:02:58 2002
***************************************************************************/
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
}
unit GraphType;
{$mode objfpc}{$H+}
interface
uses
FPCAdds, Classes, SysUtils, LCLType, LCLProc, types;
{$ifdef Trace}
{$ASSERTIONS ON}
{$endif}
type
TGraphicsColor = -$7FFFFFFF-1..$7FFFFFFF;
TGraphicsFillStyle =
(
fsSurface, // fill till the color (it fills all execpt this color)
fsBorder // fill this color (it fills only conneted pixels of this color)
);
TGraphicsBevelCut =
(
bvNone,
bvLowered,
bvRaised,
bvSpace
);
TGraphicsDrawEffect =
(
gdeNormal, // no effect
gdeDisabled, // grayed image
gdeHighlighted, // a bit highlighted image
gdeShadowed, // a bit shadowed image
gde1Bit // 1 Bit image (for non-XP windows buttons)
);
//------------------------------------------------------------------------------
// raw image data
type
{ Colorformat: Higher values means higher intensity.
For example: Red=0 means no red, Alpha=0 means transparent }
TRawImageColorFormat = (
ricfNone, // Uninitialized
ricfRGBA, // one pixel contains red, green, blue and alpha
// If AlphaPrec=0 then there is no alpha.
// Same for RedPrec, GreenPrec and BluePrec.
ricfGray // R=G=B. The Red stores the Gray. AlphaPrec can be >0.
);
TRawImageByteOrder = (
riboLSBFirst, // least significant byte first
riboMSBFirst // most significant byte first
);
TRawImageBitOrder = (
riboBitsInOrder, // Bit 0 is pixel 0
riboReversedBits // Bit 0 is pixel 7 (Bit 1 is pixel 6, ...)
);
TRawImageLineEnd = (
rileTight, // no gap at end of lines
rileByteBoundary, // each line starts at byte boundary. For example:
// If BitsPerPixel=3 and Width=1, each line has a gap
// of 5 unused bits at the end.
rileWordBoundary, // each line starts at word (16bit) boundary
rileDWordBoundary, // each line starts at double word (32bit) boundary
rileQWordBoundary, // each line starts at quad word (64bit) boundary
rileDQWordBoundary // each line starts at double quad word (128bit) boundary
);
TRawImageLineOrder = (
riloTopToBottom, // The line 0 is the top line
riloBottomToTop // The line 0 is the bottom line
);
TRawImageQueryFlag = (
riqfMono, // Include a description for a mono image
riqfGrey, // Include a description for a grey image
riqfRGB, // Include a description for a RGB image
riqfAlpha, // Include a description for an Alpha channel
riqfMask, // Include a description for a Mask
riqfPalette, // Include a description for a Palette
riqfUpdate // Update given description (instead of clearing it)
);
TRawImageQueryFlags = set of TRawImageQueryFlag;
{ TRawImageDescription }
TRawImageDescription = object
Format: TRawImageColorFormat;
Width: cardinal;
Height: cardinal;
Depth: Byte; // used bits per pixel
BitOrder: TRawImageBitOrder;
ByteOrder: TRawImageByteOrder;
LineOrder: TRawImageLineOrder;
LineEnd: TRawImageLineEnd;
BitsPerPixel: Byte; // bits per pixel. can be greater than Depth.
RedPrec: Byte; // red or gray precision. bits for red
RedShift: Byte; // bitshift. Direction: from least to most significant
GreenPrec: Byte;
GreenShift: Byte;
BluePrec: Byte;
BlueShift: Byte;
AlphaPrec: Byte;
AlphaShift: Byte;
// The next values are only valid, if there is a mask (MaskBitsPerPixel > 0)
// Masks are always separate with a depth of 1 bpp. One pixel can occupy
// one byte at most
// a value of 1 means that pixel is masked
// a value of 0 means the pixel value is shown
MaskBitsPerPixel: Byte; // bits per mask pixel, usually 1, 0 when no mask
MaskShift: Byte; // the shift (=position) of the mask bit
MaskLineEnd: TRawImageLineEnd;
MaskBitOrder: TRawImageBitOrder;
// The next values are only valid, if there is a palette (PaletteColorCount > 0)
PaletteColorCount: Word; // entries in color palette. 0 when no palette.
PaletteBitsPerIndex: Byte; // bits per palette index, this can be larger than the colors used
PaletteShift: Byte; // bitshift. Direction: from least to most significant
PaletteLineEnd: TRawImageLineEnd;
PaletteBitOrder: TRawImageBitOrder;
PaletteByteOrder: TRawImageByteOrder;
// don't use a constructor here, it will break compatibility with a record
procedure Init;
// 1-bit mono format
procedure Init_BPP1(AWidth, AHeight: integer);
// 16-bits formats
procedure Init_BPP16_R5G6B5(AWidth, AHeight: integer);
// Formats in RGB order
procedure Init_BPP24_R8G8B8_BIO_TTB(AWidth, AHeight: integer);
procedure Init_BPP24_R8G8B8_BIO_TTB_UpsideDown(AWidth, AHeight: integer);
procedure Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight: integer);
procedure Init_BPP32_R8G8B8A8_BIO_TTB(AWidth, AHeight: integer);
// Formats in Windows pixels order: BGR
procedure Init_BPP24_B8G8R8_BIO_TTB(AWidth, AHeight: integer);
procedure Init_BPP24_B8G8R8_M1_BIO_TTB(AWidth, AHeight: integer);
procedure Init_BPP32_B8G8R8_BIO_TTB(AWidth, AHeight: integer);
procedure Init_BPP32_B8G8R8_M1_BIO_TTB(AWidth, AHeight: integer);
procedure Init_BPP32_B8G8R8A8_BIO_TTB(AWidth, AHeight: integer);
procedure Init_BPP32_B8G8R8A8_M1_BIO_TTB(AWidth, AHeight: integer);
function GetDescriptionFromMask: TRawImageDescription;
function GetDescriptionFromAlpha: TRawImageDescription;
//returns indices of channels in four-element array
procedure GetRGBIndices(out Ridx, Gidx, Bidx, Aidx:Byte);
function BytesPerLine: PtrUInt;
function BitsPerLine: PtrUInt;
function MaskBytesPerLine: PtrUInt;
function MaskBitsPerLine: PtrUInt;
function AsString: string;
function IsEqual(ADesc: TRawImageDescription): Boolean;
end;
PRawImageDescription = ^TRawImageDescription;
// Note: not all devices/images have all parts at any time. But if a part can
// be applied to the device/image, the 'Description' describes its structure.
TRawImagePosition = record
Byte: PtrUInt;
Bit: cardinal;
end;
PRawImagePosition = ^TRawImagePosition;
{ TRawImage }
TRawImage = object
Description: TRawImageDescription;
Data: PByte;
DataSize: PtrUInt;
Mask: PByte;
MaskSize: PtrUInt;
Palette: PByte;
PaletteSize: PtrUInt;
// don't use a constructor here, it will break compatibility with a record
procedure Init;
procedure CreateData(AZeroMem: Boolean);
procedure FreeData;
procedure ReleaseData;
procedure ExtractRect(const ARect: TRect; out ADst: TRawImage);
function GetLineStart(ALine: Cardinal): PByte;
procedure PerformEffect(const ADrawEffect: TGraphicsDrawEffect; CreateNewData: Boolean = True; FreeOldData: boolean = false);
function ReadBits(const APosition: TRawImagePosition; APrec, AShift: Byte): Word;
procedure ReadChannels(const APosition: TRawImagePosition; out ARed, AGreen, ABlue, AAlpha: Word);
procedure ReadMask(const APosition: TRawImagePosition; out AMask: Boolean);
procedure WriteBits(const APosition: TRawImagePosition; APrec, AShift: Byte; ABits: Word);
procedure WriteChannels(const APosition: TRawImagePosition; ARed, AGreen, ABlue, AAlpha: Word);
procedure WriteMask(const APosition: TRawImagePosition; AMask: Boolean);
function IsMasked(ATestPixels: Boolean): Boolean;
function IsTransparent(ATestPixels: Boolean): Boolean;
function IsEqual(AImage: TRawImage): Boolean;
end;
PRawImage = ^TRawImage;
{ TRawImageLineStarts }
TRawImageLineStarts = object
private
FWidth: Cardinal;
FHeight: Cardinal;
FBitsPerPixel: Byte;
FLineEnd: TRawImageLineEnd;
FLineOrder: TRawImageLineOrder;
public
Positions: array of TRawImagePosition;
// don't use a constructor here, it will break compatibility with a record
procedure Init(AWidth, AHeight: cardinal; ABitsPerPixel: Byte; ALineEnd: TRawImageLineEnd; ALineOrder: TRawImageLineOrder);
function GetPosition(x, y: cardinal): TRawImagePosition;
end;
PRawImageLineStarts = ^TRawImageLineStarts;
const
DefaultByteOrder = {$IFDEF Endian_Little}riboLSBFirst{$ELSE}riboMSBFirst{$ENDIF};
function GetBytesPerLine(AWidth: Cardinal; ABitsPerPixel: Byte; ALineEnd: TRawImageLineEnd): PtrUInt;
function GetBitsPerLine(AWidth: Cardinal; ABitsPerPixel: Byte; ALineEnd: TRawImageLineEnd): PtrUInt;
function CopyImageData(AWidth, AHeight, ARowStride: Integer; ABPP: Word;
ASource: Pointer; const ARect: TRect; ASourceOrder: TRawImageLineOrder;
ADestinationOrder: TRawImageLineOrder; ADestinationEnd: TRawImageLineEnd;
out ADestination: Pointer; out ASize: PtrUInt): Boolean;
function RawImageQueryFlagsToString(AFlags: TRawImageQueryFlags): string;
var
MissingBits: array[0..15] of array[0..7] of word;
implementation
uses
Math;
{------------------------------------------------------------------------------
Function: CopyImageData
------------------------------------------------------------------------------}
function CopyImageData(AWidth, AHeight, ARowStride: Integer; ABPP: Word; ASource: Pointer; const ARect: TRect; ASourceOrder: TRawImageLineOrder;
ADestinationOrder: TRawImageLineOrder; ADestinationEnd: TRawImageLineEnd; out ADestination: Pointer; out ASize: PtrUInt): Boolean;
const
SIZEMAP: array[TRawImageLineEnd] of Byte = (
0, 0, 1, 3, 7, 15
);
var
W, H, RS, x, LineBytes, LineCount, CopySize, ZeroSize, DstRowInc: Integer;
P, DstRowPtr: PByte;
ShiftL, ShiftR: Byte;
SrcPtr: PByte absolute ASource;
DstPtr: PByte absolute ADestination;
begin
// check if we are within bounds
Result := False;
if ARect.Left < 0 then Exit;
if ARect.Top < 0 then Exit;
W := ARect.Right - ARect.Left;
H := ARect.Bottom - ARect.Top;
if W < 0 then Exit;
if H < 0 then Exit;
// calc destination rowstride
RS := (W * ABPP + 7) shr 3;
x := RS and SIZEMAP[ADestinationEnd];
if x <> 0
then Inc(RS, 1 + SIZEMAP[ADestinationEnd] - x);
// check if we can copy all
if (ARect.Left = 0) and (ARect.Top = 0)
and (ARect.Right = AWidth) and (ARect.Bottom = AHeight)
and (ASourceOrder = ADestinationOrder)
and (ARowStride = RS)
then begin
// full copy
ASize := AHeight * ARowStride;
GetMem(ADestination, ASize);
Move(ASource^, ADestination^, ASize);
Exit(True);
end;
// partial copy
// calc numer of lines to copy
if AHeight - ARect.Top < H
then LineCount := AHeight - ARect.Top
else LineCount := H;
ASize := H * RS;
GetMem(ADestination, ASize);
if (W = AWidth)
and (ASourceOrder = ADestinationOrder)
and (RS = ARowStride)
then begin
// easy case, only LineCount lines to copy
CopySize := LineCount * ARowStride;
ZeroSize := ASize - CopySize;
if ASourceOrder = riloTopToBottom
then begin
// top to bottom, adjust start
Inc(SrcPtr, ARect.Top * ARowStride);
Move(SrcPtr[0], DstPtr[0], CopySize);
// wipe remaining
if ZeroSize > 0
then FillChar(DstPtr[CopySize], ZeroSize, 0);
end
else begin
// bottom to top
// wipe remaining
if ZeroSize > 0
then FillChar(DstPtr[0], ZeroSize, 0);
x := AHeight - ARect.Bottom;
if x > 0
then Inc(SrcPtr, x * ARowStride);
Move(SrcPtr[0], DstPtr[ZeroSize], CopySize);
end;
Exit(True);
end;
// calc number of bytes to copy
// and wipe destination when source width is smaller than destination
// I'm to lazy to zero line by line, so we might do to much here
if AWidth < W
then begin
LineBytes := ((AWidth - ARect.Left) * ABPP + 7) shr 3;
FillByte(DstPtr[0], ASize, 0);
end
else begin
LineBytes := Min(RS, ARowStride);
if H <> LineCount
then FillByte(DstPtr[0], ASize, 0);
end;
// move to start line
DstRowPtr := ADestination;
if ASourceOrder = riloTopToBottom
then begin
Inc(SrcPtr, ARect.Top * ARowStride);
end
else begin
x := AHeight - ARect.Bottom;
if x >= 0
then Inc(SrcPtr, x * ARowStride)
else Inc(DstRowPtr, -x * RS);
end;
// check source and destionation order
if ASourceOrder = ADestinationOrder
then begin
DstRowInc := RS;
end
else begin
// reversed, so fill destination backwards
DstRowInc := -RS;
Inc(DstRowPtr, (LineCount - 1) * RS);
end;
// move to left pixel
Inc(SrcPtr, (ARect.Left * ABPP) shr 3);
// check if we can do byte copies
ShiftL := (ARect.Left * ABPP) and 7;
if ShiftL = 0
then begin
// Partial width, byte aligned
while LineCount > 0 do
begin
Move(SrcPtr^, DstRowPtr^, LineBytes);
Inc(SrcPtr, ARowStride);
Inc(DstRowPtr, DstRowInc);
Dec(LineCount);
end;
Exit(True);
end;
// Partial width, not aligned
ShiftR := 8 - ShiftL;
while LineCount > 0 do
begin
P := DstRowPtr;
for x := 0 to RS - 1 do
begin
P^ := Byte(SrcPtr[x] shl ShiftL) or Byte(SrcPtr[x+1] shr ShiftR);
Inc(P);
end;
Inc(SrcPtr, ARowStride);
Inc(DstRowPtr, DstRowInc);
Dec(LineCount);
end;
Result := True;
end;
function RawImageQueryFlagsToString(AFlags: TRawImageQueryFlags): string;
begin
Result := '';
if riqfMono in AFlags then Result := Result + 'riqfMono ';
if riqfGrey in AFlags then Result := Result + 'riqfGrey ';
if riqfRGB in AFlags then Result := Result + 'riqfRGB ';
if riqfAlpha in AFlags then Result := Result + 'riqfAlpha ';
if riqfMask in AFlags then Result := Result + 'riqfMask ';
if riqfPalette in AFlags then Result := Result + 'riqfPalette ';
if riqfUpdate in AFlags then Result := Result + 'riqfUpdate ';
end;
{------------------------------------------------------------------------------
Function: GetBytesPerLine
------------------------------------------------------------------------------}
function GetBytesPerLine(AWidth: Cardinal; ABitsPerPixel: Byte; ALineEnd: TRawImageLineEnd): PtrUInt;
begin
Result := (GetBitsPerLine(AWidth, ABitsPerPixel, ALineEnd) + 7) shr 3;
end;
{------------------------------------------------------------------------------
Function: GetBitsPerLine
------------------------------------------------------------------------------}
function GetBitsPerLine(AWidth: Cardinal; ABitsPerPixel: Byte; ALineEnd: TRawImageLineEnd): PtrUInt;
begin
Result := AWidth * ABitsPerPixel;
case ALineEnd of
rileTight: ;
rileByteBoundary: Result := (Result + 7) and not PtrUInt(7);
rileWordBoundary: Result := (Result + 15) and not PtrUInt(15);
rileDWordBoundary: Result := (Result + 31) and not PtrUInt(31);
rileQWordBoundary: Result := (Result + 63) and not PtrUInt(63);
rileDQWordBoundary: Result := (Result +127) and not PtrUInt(127);
end;
end;
{------------------------------------------------------------------------------
Function: RawImage_ReadBits
------------------------------------------------------------------------------}
procedure RawImage_ReadBits(AData: PByte; const APosition: TRawImagePosition;
ABitsPerPixel, APrec, AShift: Byte;
ABitOrder: TRawImageBitOrder; out ABits: Word);
var
PB: PByte;
PW: PWord absolute PB;
PC: PCardinal absolute PB;
PrecMask: Word;
begin
PrecMask := (Word(1) shl APrec) - 1;
PB := @AData[APosition.Byte];
case ABitsPerPixel of
1,2,4:
begin
if ABitOrder = riboBitsInOrder then
ABits := (PB^ shr (AShift + APosition.Bit)) and PrecMask
else
ABits := (PB^ shr (AShift + 7 - APosition.Bit)) and PrecMask;
end;
8: begin
ABits := (PB^ shr AShift) and PrecMask;
end;
16: begin
{$IFDEF VerboseLCLTodos}{$note check endian and/or source byte order}{$ENDIF}
ABits := (PW^ shr AShift) and PrecMask;
end;
32: begin
{$IFDEF VerboseLCLTodos}{$note check endian and/or source byte order}{$ENDIF}
ABits := (PC^ shr AShift) and PrecMask;
end;
else
ABits:=0;
end;
if APrec<16
then begin
// add missing bits
ABits := ABits shl (16 - APrec);
ABits := ABits or MissingBits[APrec, ABits shr 13];
end;
end;
{------------------------------------------------------------------------------
Function: RawImage_WriteBits
------------------------------------------------------------------------------}
procedure RawImage_WriteBits(AData: PByte; const APosition: TRawImagePosition;
ABitsPerPixel, APrec, AShift: Byte;
ABitOrder: TRawImageBitOrder; ABits: Word);
var
PB: PByte;
PW: PWord absolute PB;
PC: PCardinal absolute PB;
PrecMask: Cardinal;
BitShift: Integer;
begin
PB := @AData[APosition.Byte];
PrecMask := (Cardinal(1) shl APrec) - 1;
ABits := ABits shr (16 - APrec);
case ABitsPerPixel of
1,2,4:
begin
if ABitOrder = riboBitsInOrder
then BitShift := AShift + APosition.Bit
else BitShift := AShift + 7 - APosition.Bit;
PrecMask := not(PrecMask shl BitShift);
PB^ := (PB^ and PrecMask) or (ABits shl BitShift);
end;
8: begin
PrecMask := not(PrecMask shl aShift);
PB^ := (PB^ and PrecMask) or (ABits shl AShift);
end;
16: begin
{$IFDEF VerboseLCLTodos}{$note check endian and/or source byte order}{$ENDIF}
PrecMask := not(PrecMask shl AShift);
PW^ := (PW^ and PrecMask) or (ABits shl AShift);
end;
32: begin
{$IFDEF VerboseLCLTodos}{$note check endian and/or source byte order}{$ENDIF}
PrecMask := not(PrecMask shl AShift);
PC^ := (PC^ and PrecMask) or (ABits shl AShift);
end;
end;
end;
{ TRawImageDescription }
procedure TRawImageDescription.Init;
begin
FillChar(Self, SizeOf(Self), 0);
end;
// 1-bit mono format
procedure TRawImageDescription.Init_BPP1(AWidth, AHeight: integer);
begin
// setup an artificial ScanLineImage with format RGB 24 bit, 24bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfGray;
Depth := 1; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 1; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 1; // grey precision
RedShift := 0;
GreenPrec := 0;
GreenShift := 0; // bitshift. Direction: from least to most significant
BluePrec := 0;
BlueShift:=0;
// AlphaPrec:=0;
// MaskBitsPerPixel:=0;
end;
procedure TRawImageDescription.Init_BPP16_R5G6B5(AWidth, AHeight: integer);
begin
// setup an artificial ScanLineImage with format RGB 24 bit, 24bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 16; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 24; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 5; // red precision. bits for red
RedShift := 0;
GreenPrec := 6;
GreenShift := 5; // bitshift. Direction: from least to most significant
BluePrec := 5;
BlueShift:=11;
// AlphaPrec:=0;
// MaskBitsPerPixel:=0;
end;
procedure TRawImageDescription.Init_BPP24_R8G8B8_BIO_TTB(AWidth, AHeight: integer);
begin
// setup an artificial ScanLineImage with format RGB 24 bit, 24bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 24; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 24; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 8; // red precision. bits for red
RedShift := 0;
GreenPrec := 8;
GreenShift := 8; // bitshift. Direction: from least to most significant
BluePrec := 8;
BlueShift:=16;
// AlphaPrec:=0;
// MaskBitsPerPixel:=0;
end;
procedure TRawImageDescription.Init_BPP24_R8G8B8_BIO_TTB_UpsideDown(AWidth, AHeight: integer);
begin
// setup an artificial ScanLineImage with format RGB 24 bit, 24bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 24; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloBottomToTop;
BitsPerPixel := 24; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 8; // red precision. bits for red
RedShift := 0;
GreenPrec := 8;
GreenShift := 8; // bitshift. Direction: from least to most significant
BluePrec := 8;
BlueShift:=16;
// AlphaPrec:=0;
// MaskBitsPerPixel:=0;
end;
procedure TRawImageDescription.Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight: integer);
begin
// setup an artificial ScanLineImage with format RGB 32 bit, 32bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 32; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 32; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 8; // red precision. bits for red
RedShift := 8;
GreenPrec := 8;
GreenShift := 16; // bitshift. Direction: from least to most signifikant
BluePrec := 8;
BlueShift := 24;
AlphaPrec := 8;
AlphaShift := 0;
// MaskBitsPerPixel := 0;
end;
procedure TRawImageDescription.Init_BPP32_R8G8B8A8_BIO_TTB(AWidth, AHeight: integer);
begin
// setup an artificial ScanLineImage with format RGB 32 bit, 32bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 32; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 32; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 8; // red precision. bits for red
RedShift := 0;
GreenPrec := 8;
GreenShift := 8; // bitshift. Direction: from least to most signifikant
BluePrec := 8;
BlueShift := 16;
AlphaPrec := 8;
AlphaShift := 24;
// MaskBitsPerPixel := 0;
end;
procedure TRawImageDescription.Init_BPP24_B8G8R8_BIO_TTB(AWidth, AHeight: integer);
{ pf24bit:
Format=ricfRGBA HasPalette=false Depth=24 PaletteColorCount=0
BitOrder=riboBitsInOrder ByteOrder=DefaultByteOrder
LineOrder=riloTopToBottom
BitsPerPixel=24 LineEnd=rileDWordBoundary
RedPrec=8 RedShift=16 GreenPrec=8 GreenShift=8 BluePrec=8 BlueShift=0
}
begin
// setup an artificial ScanLineImage with format RGB 24 bit, 24bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 24; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 24; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 8; // red precision. bits for red
RedShift := 16;
GreenPrec := 8;
GreenShift := 8; // bitshift. Direction: from least to most significant
BluePrec := 8;
// BlueShift:=0;
// AlphaPrec:=0;
// MaskBitsPerPixel:=0;
end;
procedure TRawImageDescription.Init_BPP24_B8G8R8_M1_BIO_TTB(AWidth, AHeight: integer);
{ pf24bit:
Format=ricfRGBA HasPalette=false Depth=24 PaletteColorCount=0
BitOrder=riboBitsInOrder ByteOrder=DefaultByteOrder
LineOrder=riloTopToBottom
BitsPerPixel=24 LineEnd=rileDWordBoundary
RedPrec=8 RedShift=16 GreenPrec=8 GreenShift=8 BluePrec=8 BlueShift=0
Masked
}
begin
// setup an artificial ScanLineImage with format RGB 24 bit, 24bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 24; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 24; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 8; // red precision. bits for red
RedShift := 16;
GreenPrec := 8;
GreenShift := 8; // bitshift. Direction: from least to most significant
BluePrec := 8;
// BlueShift := 0;
// AlphaPrec := 0;
MaskBitsPerPixel := 1;
MaskBitOrder := riboBitsInOrder;
// MaskShift := 0; // the shift (=position) of the mask bit
MaskLineEnd := rileWordBoundary;
end;
procedure TRawImageDescription.Init_BPP32_B8G8R8_BIO_TTB(AWidth, AHeight: integer);
{ pf32bit:
Format=ricfRGBA HasPalette=false Depth=24 PaletteColorCount=0
BitOrder=riboBitsInOrder ByteOrder=DefaultByteOrder
LineOrder=riloTopToBottom
BitsPerPixel=32 LineEnd=rileDWordBoundary
RedPrec=8 RedShift=16 GreenPrec=8 GreenShift=8 BluePrec=8 BlueShift=0
No alpha
No mask
}
begin
// setup an artificial ScanLineImage with format RGB 24 bit, 32bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 24; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 32; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 8; // red precision. bits for red
RedShift := 16;
GreenPrec := 8;
GreenShift := 8; // bitshift. Direction: from least to most signifikant
BluePrec := 8;
// BlueShift := 0;
// AlphaPrec := 0;
// MaskBitsPerPixel:=0;
end;
procedure TRawImageDescription.Init_BPP32_B8G8R8_M1_BIO_TTB(AWidth, AHeight: integer);
{ pf32bit:
Format=ricfRGBA HasPalette=false Depth=24 PaletteColorCount=0
BitOrder=riboBitsInOrder ByteOrder=DefaultByteOrder
LineOrder=riloTopToBottom
BitsPerPixel=32 LineEnd=rileDWordBoundary
RedPrec=8 RedShift=16 GreenPrec=8 GreenShift=8 BluePrec=8 BlueShift=0
no alpha
with mask
}
begin
// setup an artificial ScanLineImage with format RGB 24 bit, 32bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 24; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 32; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 8; // red precision. bits for red
RedShift := 16;
GreenPrec := 8;
GreenShift := 8; // bitshift. Direction: from least to most signifikant
BluePrec := 8;
// BlueShift := 0;
// AlphaPrec := 0;
MaskBitsPerPixel := 1;
MaskBitOrder := riboBitsInOrder;
// MaskShift := 0; // the shift (=position) of the mask bit
MaskLineEnd := rileWordBoundary;
end;
function TRawImageDescription.MaskBitsPerLine: PtrUInt;
begin
Result := GetBitsPerLine(Width, MaskBitsPerPixel, MaskLineEnd);
end;
function TRawImageDescription.MaskBytesPerLine: PtrUInt;
begin
Result := (GetBitsPerLine(Width, MaskBitsPerPixel, MaskLineEnd) + 7) shr 3;
end;
procedure TRawImageDescription.Init_BPP32_B8G8R8A8_BIO_TTB(AWidth, AHeight: integer);
{ pf32bit:
Format=ricfRGBA HasPalette=false Depth=32 PaletteColorCount=0
BitOrder=riboBitsInOrder ByteOrder=DefaultByteOrder
LineOrder=riloTopToBottom
BitsPerPixel=32 LineEnd=rileDWordBoundary
RedPrec=8 RedShift=16 GreenPrec=8 GreenShift=8 BluePrec=8 BlueShift=0
alpha
no mask
}
begin
// setup an artificial ScanLineImage with format RGB 32 bit, 32bit depth format
FillChar(Self, SizeOf(Self), 0);
Format := ricfRGBA;
Depth := 32; // used bits per pixel
Width := AWidth;
Height := AHeight;
BitOrder := riboBitsInOrder;
ByteOrder := riboLSBFirst;
LineOrder := riloTopToBottom;
BitsPerPixel := 32; // bits per pixel. can be greater than Depth.
LineEnd := rileDWordBoundary;
RedPrec := 8; // red precision. bits for red
RedShift := 16;
GreenPrec := 8;
GreenShift := 8; // bitshift. Direction: from least to most signifikant
BluePrec := 8;
BlueShift := 0;
AlphaPrec := 8;
AlphaShift := 24;
// MaskBitsPerPixel := 0;
end;
procedure TRawImageDescription.Init_BPP32_B8G8R8A8_M1_BIO_TTB(AWidth, AHeight: integer);
begin
Init_BPP32_B8G8R8A8_BIO_TTB(AWidth, AHeight);
MaskBitsPerPixel := 1;
MaskBitOrder := riboBitsInOrder;
MaskLineEnd := rileWordBoundary;
end;
function TRawImageDescription.GetDescriptionFromMask: TRawImageDescription;
begin
Result{%H-}.Init;
Result.Format := ricfGray;
Result.Width := Width;
Result.Height := Height;
Result.Depth := 1; // per def
Result.BitOrder := MaskBitOrder;
Result.ByteOrder := DefaultByteOrder;
Result.LineOrder := LineOrder;
Result.LineEnd := MaskLineEnd;
Result.BitsPerPixel := MaskBitsPerPixel;
Result.RedPrec := 1;
Result.RedShift := MaskShift;
end;
function TRawImageDescription.BitsPerLine: PtrUInt;
begin
Result := GetBitsPerLine(Width, BitsPerPixel, LineEnd);
end;
function TRawImageDescription.BytesPerLine: PtrUInt;
begin
Result := (GetBitsPerLine(Width, BitsPerPixel, LineEnd) + 7) shr 3;
end;
function TRawImageDescription.GetDescriptionFromAlpha: TRawImageDescription;
begin
Result{%H-}.Init;
Result.Format := ricfGray;
Result.Width := Width;
Result.Height := Height;
Result.Depth := AlphaPrec;
Result.BitOrder := BitOrder;
Result.ByteOrder := ByteOrder;
Result.LineOrder := LineOrder;
Result.LineEnd := LineEnd;
Result.BitsPerPixel := BitsPerPixel;
Result.RedPrec := AlphaPrec;
Result.RedShift := AlphaShift;
end;
procedure TRawImageDescription.GetRGBIndices(out Ridx, Gidx, Bidx, Aidx: Byte);
const
// riboLSBFirst, riboMSBFirst
COMPONENT_MASK: array[TRawImageByteOrder] of Byte = (0, 3);
begin
Ridx := (RedShift shr 3) xor COMPONENT_MASK[ByteOrder];
Gidx := (GreenShift shr 3) xor COMPONENT_MASK[ByteOrder];
Bidx := (BlueShift shr 3) xor COMPONENT_MASK[ByteOrder];
Aidx := (AlphaShift shr 3) xor COMPONENT_MASK[ByteOrder];
end;
function TRawImageDescription.AsString: string;
function EnumToString(AEnum: TRawImageColorFormat): string;
begin
WriteStr(Result, AEnum);
end;
function EnumToString(AEnum: TRawImageByteOrder): string;
begin
WriteStr(Result, AEnum);
end;
function EnumToString(AEnum: TRawImageBitOrder): string;
begin
WriteStr(Result, AEnum);
end;
function EnumToString(AEnum: TRawImageLineEnd): string;
begin
WriteStr(Result, AEnum);
end;
function EnumToString(AEnum: TRawImageLineOrder): string;
begin
WriteStr(Result, AEnum);
end;
begin
Result:=
' Format='+EnumToString(Format)
+' HasPalette->'+dbgs(PaletteColorCount <> 0)
+' HasMask->'+dbgs(PaletteColorCount <> 0)
+' Depth='+IntToStr(Depth)
+' Width='+IntToStr(Width)
+' Height='+IntToStr(Height)
+' BitOrder='+EnumToString(BitOrder)
+' ByteOrder='+EnumToString(ByteOrder)
+' LineOrder='+EnumToString(LineOrder)
+' LineEnd='+EnumToString(LineEnd)
+' BitsPerPixel='+IntToStr(BitsPerPixel)
+' BytesPerLine->'+IntToStr(GetBytesPerLine(Width,BitsPerPixel,LineEnd))
+' RedPrec='+IntToStr(RedPrec)
+' RedShift='+IntToStr(RedShift)
+' GreenPrec='+IntToStr(GreenPrec)
+' GreenShift='+IntToStr(GreenShift)
+' BluePrec='+IntToStr(BluePrec)
+' BlueShift='+IntToStr(BlueShift)
+' AlphaPrec='+IntToStr(AlphaPrec)
+' AlphaShift='+IntToStr(AlphaShift)
+' ~~~mask~~~'
+' MaskBitsPerPixel='+IntToStr(MaskBitsPerPixel)
+' MaskShift='+IntToStr(MaskShift)
+' MaskLineEnd='+EnumToString(MaskLineEnd)
+' MaskBitOrder='+EnumToString(MaskBitOrder)
+' MaskBytesPerLine->'+IntToStr(GetBytesPerLine(Width,MaskBitsPerPixel,MaskLineEnd))
+' ~~~palette~~~'
+' PaletteColorCount='+IntToStr(PaletteColorCount)
+' PaletteBitsPerIndex='+IntToStr(PaletteBitsPerIndex)
+' PaletteShift='+IntToStr(PaletteShift)
+' PaletteLineEnd='+EnumToString(PaletteLineEnd)
+' PaletteBitOrder='+EnumToString(PaletteBitOrder)
+' PaletteByteOrder='+EnumToString(PaletteByteOrder)
+' PaletteBytesPerLine->'+IntToStr(GetBytesPerLine(Width,PaletteBitsPerIndex,PaletteLineEnd))
+'';
end;
function TRawImageDescription.IsEqual(ADesc: TRawImageDescription): Boolean;
begin
// We cannot use CompareMem since some fields are depending on other fields
// Result := CompareMem(@Self, @ADescription, SizeOf(Self));
Result := False;
if Format <> ADesc.Format then Exit;
if Width <> ADesc.Width then Exit;
if Height <> ADesc.Height then Exit;
if Depth <> ADesc.Depth then Exit;
if BitOrder <> ADesc.BitOrder then Exit;
if ByteOrder <> ADesc.ByteOrder then Exit;
if LineOrder <> ADesc.LineOrder then Exit;
if LineEnd <> ADesc.LineEnd then Exit;
if BitsPerPixel <> ADesc.BitsPerPixel then Exit;
if RedPrec <> ADesc.RedPrec then Exit;
if GreenPrec <> ADesc.GreenPrec then Exit;
if BluePrec <> ADesc.BluePrec then Exit;
if AlphaPrec <> ADesc.AlphaPrec then Exit;
// The next values are only valid, if there is a precision
if (RedPrec <> 0) and (RedShift <> ADesc.RedShift ) then Exit;
if Format = ricfRGBA
then begin
// for mono images only red is of importance
if (GreenPrec <> 0) and (GreenShift <> ADesc.GreenShift) then Exit;
if (BluePrec <> 0) and (BlueShift <> ADesc.BlueShift ) then Exit;
end;
if (AlphaPrec <> 0) and (AlphaShift <> ADesc.AlphaShift) then Exit;
// The next values are only valid, if there is a mask (MaskBitsPerPixel > 0)
if MaskBitsPerPixel <> ADesc.MaskBitsPerPixel then Exit;
if MaskBitsPerPixel <> 0
then begin
if MaskShift <> ADesc.MaskShift then Exit;
if MaskLineEnd <> ADesc.MaskLineEnd then Exit;
if MaskBitOrder <> ADesc.MaskBitOrder then Exit;
end;
// The next values are only valid, if there is a palette (PaletteColorCount > 0)
if PaletteColorCount <> ADesc.PaletteColorCount then Exit;
if PaletteColorCount <> 0
then begin
if PaletteBitsPerIndex <> ADesc.PaletteBitsPerIndex then Exit;
if PaletteShift <> ADesc.PaletteShift then Exit;
if PaletteLineEnd <> ADesc.PaletteLineEnd then Exit;
if PaletteBitOrder <> ADesc.PaletteBitOrder then Exit;
if PaletteByteOrder <> ADesc.PaletteByteOrder then Exit;
end;
Result := True;
end;
{ TRawImage }
function TRawImage.IsMasked(ATestPixels: Boolean): Boolean;
function CheckMask: boolean;
var
Width: cardinal;
Height: cardinal;
UsedBitsPerLine: cardinal;
TotalBits: Cardinal;
TotalBitsPerLine: cardinal;
TotalBytesPerLine: cardinal;
UnusedBitsAtEnd: Byte;
UnusedBytesAtEnd: Byte;
P: PCardinal;
LinePtr: PByte;
x, y, xEnd: Integer;
EndMask: Cardinal; // No mask bits should be set. The Cardinal at line end
// can contain some unused bits. This mask AND cardinal
// at line end makes the unsused bits all 0.
procedure CreateEndMask;
begin
if Description.MaskBitOrder = riboBitsInOrder
then EndMask := ($FF shr UnusedBitsAtEnd)
else EndMask := ($FF shl UnusedBitsAtEnd) and $FF;
// add unused bytes
{$ifdef endian_big}
// read in memory -> [??][eM][uu][uu]
EndMask := ($FFFFFF00 or EndMask) shl (UnusedBytesAtEnd shl 3);
{$else}
// read in memory -> [uu][uu][eM][??]
EndMask := ((EndMask shl 24) or $00FFFFFF) shr (UnusedBytesAtEnd shl 3);
{$endif}
end;
// separate dump procs to avoid code flow cluttering
// added here in case somone want to debug
{$IFDEF VerboseRawImage}
procedure DumpFull;
begin
DebugLn('RawImageMaskIsEmpty FullByte y=',dbgs(y),' x=',dbgs(x),' Byte=',DbgS(p^));
end;
procedure DumpEdge;
begin
DebugLn('RawImageMaskIsEmpty EdgeByte y=',dbgs(y),' x=',dbgs(x),
' Byte=',HexStr(Cardinal(p^),2),
//' UnusedMask=',HexStr(Cardinal(UnusedMask),2),
//' OR='+dbgs(p^ or UnusedMask),
' UnusedBitsAtEnd='+dbgs(UnusedBitsAtEnd),
' UsedBitsPerLine='+dbgs(UsedBitsPerLine),
' Width='+dbgs(Width),
' ARawImage.Description.MaskBitsPerPixel='+dbgs(Description.MaskBitsPerPixel));
end;
{$endif}
begin
Result := True;
Width := Description.Width;
Height := Description.Height;
TotalBitsPerLine := GetBitsPerLine(Width, Description.MaskBitsPerPixel, Description.MaskLineEnd);
TotalBits := Height * TotalBitsPerLine;
if MaskSize < PtrUInt((TotalBits + 7) shr 3)
then raise Exception.Create('RawImage_IsMasked - Invalid MaskSize');
UsedBitsPerLine := Width * Description.MaskBitsPerPixel;
UnusedBitsAtEnd := TotalBitsPerLine - UsedBitsPerLine;
if UnusedBitsAtEnd = 0
then begin
// the next line follows the previous one, so we can compare the whole
// memblock in one go
P := PCardinal(Mask);
for x := 1 to TotalBits shr 5 do
begin
if p^ <> 0 then Exit;
Inc(p);
end;
// redefine UnusedBitsAtEnd as the bits at the end of the block
UnusedBitsAtEnd := TotalBits and $1F;
if UnusedBitsAtEnd <> 0
then begin
// check last piece
UnusedBytesAtEnd := UnusedBitsAtEnd shr 3;
// adjust to byte bounds
UnusedBitsAtEnd := UnusedBitsAtEnd and 7;
CreateEndMask;
if p^ and EndMask <> 0 then Exit;
end;
end
else begin
// scan each line
TotalBytesPerLine := TotalBitsPerLine shr 3;
UnusedBytesAtEnd := UnusedBitsAtEnd shr 3;
// Number of cardinals to check
xEnd := (TotalBytesPerLine - UnusedBytesAtEnd) shr 2;
// Adjust unused to only the last checked
UnusedBytesAtEnd := UnusedBytesAtEnd and 3;
UnusedBitsAtEnd := UnusedBitsAtEnd and 7;
// create mask for the last bits
CreateEndMask;
LinePtr := Mask;
for y := 0 to Height - 1 do
begin
p := PCardinal(LinePtr);
for x := 0 to xEnd - 1 do
begin
if p^ <> 0 then Exit;
Inc(p);
end;
// check last end
if (EndMask <> 0) and (p^ and EndMask <> 0) then Exit;
Inc(LinePtr, TotalBytesPerLine);
end;
end;
Result := False;
end;
begin
Result := False;
//DebugLn('RawImageMaskIsEmpty Quicktest: empty ',dbgs(RawImage^.Description.Width),'x',dbgs(RawImage^.Description.Height));
// quick test
if (Mask = nil)
or (MaskSize = 0)
or (Description.MaskBitsPerPixel = 0)
or (Description.Width = 0)
or (Description.Height = 0)
then begin
{$IFDEF VerboseRawImage}
DebugLn('RawImageMaskIsEmpty Quicktest: empty');
{$ENDIF}
exit;
end;
if ATestPixels
then begin
Result := CheckMask;
{$IFDEF VerboseRawImage}
DebugLn('RawImageMaskIsEmpty Empty=',dbgs(not Result));
{$ENDIF}
end
else begin
Result := True;
{$IFDEF VerboseRawImage}
DebugLn('RawImageMaskIsEmpty NoPixelTest: not empty');
{$ENDIF}
Exit;
end;
end;
function TRawImage.IsTransparent(ATestPixels: Boolean): Boolean;
function CheckAlpha: Boolean;
begin
{$IFDEF VerboseLCLTodos}{$note TODO: implement CheckAlpha}{$ENDIF}
Result := True;
end;
begin
Result :=
(Data <> nil) and
(DataSize <> 0) and
(Description.AlphaPrec <> 0) and
(Description.Width = 0) and
(Description.Height = 0);
if Result and ATestPixels then
Result := CheckAlpha;
end;
function TRawImage.ReadBits(const APosition: TRawImagePosition; APrec, AShift: Byte): Word;
begin
RawImage_ReadBits(Data, APosition, Description.BitsPerPixel, APrec, AShift, Description.BitOrder, Result);
end;
procedure TRawImage.ReadChannels(const APosition: TRawImagePosition; out ARed, AGreen, ABlue, AAlpha: Word);
var
D: TRawImageDescription absolute Description;
begin
case Description.Format of
ricfRGBA: begin
RawImage_ReadBits(Data, APosition, D.BitsPerPixel, D.RedPrec, D.RedShift, D.BitOrder, ARed);
RawImage_ReadBits(Data, APosition, D.BitsPerPixel, D.GreenPrec, D.GreenShift, D.BitOrder, AGreen);
RawImage_ReadBits(Data, APosition, D.BitsPerPixel, D.BluePrec, D.BlueShift, D.BitOrder, ABlue);
end;
ricfGray: begin
RawImage_ReadBits(Data, APosition, D.BitsPerPixel, D.RedPrec, D.RedShift, D.BitOrder, ARed);
AGreen := ARed;
ABlue := ARed;
end;
else
ARed := 0;
AGreen := 0;
ABlue := 0;
AAlpha := 0;
Exit;
end;
if D.AlphaPrec > 0
then RawImage_ReadBits(Data, APosition, D.BitsPerPixel, D.AlphaPrec, D.AlphaShift, D.BitOrder, AAlpha)
else AAlpha := High(AAlpha);
end;
procedure TRawImage.ReadMask(const APosition: TRawImagePosition; out AMask: Boolean);
var
D: TRawImageDescription absolute Description;
M: Word;
begin
if (D.MaskBitsPerPixel > 0) and (Mask <> nil)
then begin
RawImage_ReadBits(Mask, APosition, D.MaskBitsPerPixel, 1, D.MaskShift, D.MaskBitOrder, M);
AMask := M <> 0;
end
else AMask := False;
end;
procedure TRawImage.FreeData;
begin
FreeMem(Data);
Data := nil;
DataSize:=0;
FreeMem(Mask);
Mask := nil;
MaskSize := 0;
FreeMem(Palette);
Palette := nil;
PaletteSize:=0;
end;
procedure TRawImage.Init;
begin
Description.Init;
Data := nil;
DataSize:=0;
Mask := nil;
MaskSize := 0;
Palette := nil;
PaletteSize:=0;
end;
function TRawImage.IsEqual(AImage: TRawImage): Boolean;
begin
//Result := CompareMem(@Self, @AImage, SizeOf(Self));
Result :=
Description.IsEqual(AImage.Description) and
(DataSize = AImage.DataSize) and
(MaskSize = AImage.MaskSize) and
(PaletteSize = AImage.PaletteSize);
if Result then
Result := CompareMem(Data, AImage.Data, DataSize);
if Result then
Result := CompareMem(Mask, AImage.Mask, MaskSize);
if Result then
Result := CompareMem(Palette, AImage.Palette, PaletteSize);
end;
procedure TRawImage.ReleaseData;
begin
Data := nil;
DataSize := 0;
Mask := nil;
MaskSize := 0;
Palette := nil;
PaletteSize := 0;
end;
procedure TRawImage.WriteBits(const APosition: TRawImagePosition; APrec, AShift: Byte; ABits: Word);
begin
RawImage_WriteBits(Data, APosition, Description.BitsPerPixel, APrec, AShift, Description.BitOrder, ABits);
end;
procedure TRawImage.WriteChannels(const APosition: TRawImagePosition; ARed, AGreen, ABlue, AAlpha: Word);
var
D: TRawImageDescription absolute Description;
begin
case D.Format of
ricfRGBA: begin
RawImage_WriteBits(Data, APosition, D.BitsPerPixel, D.RedPrec, D.RedShift, D.BitOrder, ARed);
RawImage_WriteBits(Data, APosition, D.BitsPerPixel, D.GreenPrec, D.GreenShift, D.BitOrder, AGreen);
RawImage_WriteBits(Data, APosition, D.BitsPerPixel, D.BluePrec, D.BlueShift, D.BitOrder, ABlue);
end;
ricfGray: begin
RawImage_WriteBits(Data, APosition, D.BitsPerPixel, D.RedPrec, D.RedShift, D.BitOrder, ARed);
end;
else
Exit;
end;
if D.AlphaPrec = 0 then Exit;
RawImage_WriteBits(Data, APosition, D.BitsPerPixel, D.AlphaPrec, D.AlphaShift, D.BitOrder, AAlpha);
end;
procedure TRawImage.WriteMask(const APosition: TRawImagePosition; AMask: Boolean);
const
M: array[Boolean] of Word = (0, $FFFF);
var
D: TRawImageDescription absolute Description;
begin
if Mask = nil then Exit;
if D.MaskBitsPerPixel = 0 then Exit;
RawImage_WriteBits(Mask, APosition, D.MaskBitsPerPixel, 1, D.MaskShift, D.MaskBitOrder, M[AMask]);
end;
procedure TRawImage.CreateData(AZeroMem: Boolean);
var
Size: QWord;
begin
// get current size
if Description.Width = 0 then Exit;
if Description.Height = 0 then Exit;
// calculate size
with Description do
Size := GetBitsPerLine(Width, BitsPerPixel, LineEnd);
Size := (Size * Description.Height) shr 3;
if Size < High(DataSize)
then DataSize := Size
else DataSize := High(DataSize);
ReAllocMem(Data, DataSize);
if AZeroMem
then FillChar(Data^, DataSize, 0);
// Setup mask if needed
if Description.MaskBitsPerPixel = 0 then Exit;
// calculate mask size
with Description do
Size := GetBitsPerLine(Width, MaskBitsPerPixel, MaskLineEnd);
Size := (Size * Description.Height) shr 3;
if Size < High(MaskSize)
then MaskSize := Size
else MaskSize := High(MaskSize);
ReAllocMem(Mask, MaskSize);
if AZeroMem
then FillChar(Mask^, MaskSize, 0);
end;
procedure TRawImage.ExtractRect(const ARect: TRect; out ADst: TRawImage);
procedure ExtractData(AData: PByte; ADataSize: PtrUInt; ABitsPerPixel: Byte;
ABitOrder: TRawImageBitOrder; ALineEnd: TRawImageLineEnd;
ADest: PByte; ADestSize: PtrUInt);
var
SrcWidth, SrcHeight, SrcRight: LongInt;
DstWidth, DstHeight: LongInt;
x, y: Integer;
LineOrder: TRawImageLineOrder;
SrcLineStarts, DstLineStarts: TRawImageLineStarts;
SrcStartPos, SrcEndPos, DstStartPos: TRawImagePosition;
Shift0, Shift1: Byte;
DstW1: Word;
SrcPos: PByte;
DstPos: PByte;
ByteCount: PtrUInt;
begin
SrcWidth := Description.Width;
DstWidth := ADst.Description.Width;
LineOrder := Description.LineOrder;
//DebugLn'ExtractRawImageDataRect data=',DbgS(DestData),' Size=',DestDataSize);
if (SrcWidth = DstWidth) and (ARect.Top = 0)
then begin
if LineOrder = riloTopToBottom
then // copy whole source from beginning
System.Move(AData[0], ADest[0], ADestSize)
else // copy remainder
System.Move(AData[ADataSize - ADestSize], ADest[0], ADestSize);
Exit;
end;
SrcHeight := Description.Height;
DstHeight := ADst.Description.Height;
// calculate line starts
if LineOrder = riloTopToBottom
then // we only need the first part from start
SrcLineStarts.Init(SrcWidth, ARect.Top + DstHeight, ABitsPerPixel, ALineEnd, LineOrder)
else
SrcLineStarts.Init(SrcWidth, SrcHeight - ARect.Top, ABitsPerPixel, ALineEnd, LineOrder);
DstLineStarts.Init(DstWidth, DstHeight, ABitsPerPixel, ALineEnd, LineOrder);
// copy
SrcRight := ARect.Left + DstWidth;
for y := 0 to DstHeight - 1 do
begin
SrcStartPos := SrcLineStarts.GetPosition(ARect.Left, y + ARect.Top);
SrcEndPos := SrcLineStarts.GetPosition(SrcRight, y + ARect.Top);
DstStartPos := DstLineStarts.GetPosition(0, y);
//DebugLn'ExtractRawImageDataRect A y=',y,' SrcByte=',SrcLineStartPosition.Byte,' SrcBit=',SrcLineStartPosition.Bit,
//' DestByte=',DestLineStartPosition.Byte,' DestBit=',DestLineStartPosition.Bit);
if (SrcStartPos.Bit = 0) and (DstStartPos.Bit = 0)
then begin
// copy bytes
ByteCount := SrcEndPos.Byte - SrcStartPos.Byte;
if SrcEndPos.Bit > 0
then Inc(ByteCount);
//DebugLn'ExtractRawImageDataRect B ByteCount=',ByteCount);
System.Move(AData[SrcStartPos.Byte], ADest[DstStartPos.Byte], ByteCount);
end
else if DstStartPos.Bit = 0
then begin
// copy and move bits
ByteCount := (DstWidth * ABitsPerPixel + 7) shr 3;
SrcPos := @AData[SrcStartPos.Byte];
DstPos := @ADest[DstStartPos.Byte];
Shift0 := SrcStartPos.Bit;
Shift1 := 8 - Shift0;
if ABitOrder = riboBitsInOrder
then begin
// src[byte|bit]: 07 06 05 04 03 02 01 00 :: 17 16 15 14 13 12 11 10 :
// imagine startbit = 3 ->
// dst[byte|bit]: 12 11 10 07 06 05 04 03 :
for x := 0 to ByteCount - 1 do
begin
DstW1 := SrcPos[0] shr Shift0;
DstPos^ := Byte(DstW1 or (SrcPos[1] shl Shift1));
inc(SrcPos);
inc(DstPos);
end;
end
else begin
// src[byte|bit]: 07 06 05 04 03 02 01 00 :: 17 16 15 14 13 12 11 10 :
// imagine startbit = 3 ->
// dst[byte|bit]: 04 03 02 01 00 17 16 15 :
for x := 0 to ByteCount - 1 do
begin
DstW1 := SrcPos[0] shl Shift0;
DstPos^ := Byte(DstW1 or (SrcPos[1] shr Shift1));
inc(SrcPos);
inc(DstPos);
end;
end;
end
else begin
{$IFNDEF DisableChecks}
DebugLn('ToDo: ExtractRawImageRect DestLineStartPosition.Bit>0');
{$ENDIF}
break;
end;
end;
end;
var
R: TRect;
begin
//DebugLn'ExtractRawImageRect SrcRawImage=',RawImageDescriptionAsString(@SrcRawImage^.Description),
// ' SrcRect=',SrcRect.Left,',',SrcRect.Top,',',SrcRect.Right,',',SrcRect.Bottom);
// copy description
ADst.Description := Description;
ADst.ReleaseData;
// get intersection
IntersectRect(R, Rect(0, 0, Description.Width, Description.Height), ARect);
ADst.Description.Width := R.Right - R.Left;
ADst.Description.Height := R.Bottom - R.Top;
if (ADst.Description.Width <= 0)
or (ADst.Description.Height <= 0)
then begin
ADst.Description.Width := 0;
ADst.Description.Height := 0;
Exit;
end;
if Data = nil then Exit;
if DataSize = 0 then Exit;
// allocate some space
ADst.CreateData(False);
// extract rectangle from Data
ExtractData(Data, DataSize, Description.BitsPerPixel, Description.BitOrder,
Description.LineEnd, ADst.Data, ADst.DataSize);
// extract rectangle from MAsk
if Description.MaskBitsPerPixel = 0 then Exit;
if Mask = nil then Exit;
if MaskSize = 0 then Exit;
//DebugLn'ExtractRawImageRect Mask SrcRawImage=',RawImageDescriptionAsString(@SrcMaskDesc));
ExtractData(Mask, MaskSize, Description.MaskBitsPerPixel, Description.MaskBitOrder,
Description.MaskLineEnd, ADst.Mask, ADst.MaskSize);
end;
function TRawImage.GetLineStart(ALine: Cardinal): PByte;
begin
Result := Data;
if Result = nil then Exit;
if ALine = 0 then Exit;
Inc(Result, ALine * Description.BytesPerLine);
end;
procedure TRawImage.PerformEffect(const ADrawEffect: TGraphicsDrawEffect;
CreateNewData: Boolean; FreeOldData: boolean);
function CheckDescription: Boolean;
begin
Result :=
(Description.Format = ricfRGBA) and
(Description.PaletteColorCount = 0) and
(Description.MaskBitsPerPixel = 0) and
(Description.Depth = 32) and
(Description.BitOrder = riboBitsInOrder) and
(Description.ByteOrder = riboMSBFirst) and
(Description.LineOrder = riloTopToBottom) and
(Description.BitsPerPixel = 32) and
(Description.RedPrec = 8) and
(Description.RedShift = 8) and
(Description.GreenPrec = 8) and
(Description.GreenShift = 16) and
(Description.BluePrec = 8) and
(Description.BlueShift = 24) and
(Description.AlphaPrec = 8) and
(Description.AlphaShift = 0);
end;
const
Glow = 68;
Shadow = 48;
GlowColorMultiplier = (256 - Glow) / 256;
ShadowColorMultiplier = (256 - Shadow) / 256;
// 1 Bit color weights. Total weight = 1000
R_Weight: Word = $00DE;
G_Weight: Word = $02C3;
B_Weight: Word = $0047;
H_Threshold = $D5; // threshold of highlight ($D5 is value from experiments. $80 is standard)
var
AData: PRGBAQuad;
P: Pointer;
i, j: integer;
begin
// check here for Description. Only RGBA data can be processed here.
if not CheckDescription then
Exit;
if CreateNewData then
begin
GetMem(AData, DataSize);
Move(Data^, AData^, DataSize);
P := AData;
end
else begin
P := Data;
AData := P;
end;
case ADrawEffect of
gdeNormal: ;
gdeDisabled:
begin
for i := 0 to Description.Height - 1 do
for j := 0 to Description.Width - 1 do
begin
with AData^ do
begin
Red := (Red + Green + Blue) div 3;
Green := Red;
Blue := Red;
end;
inc(AData);
end;
end;
gdeHighlighted:
begin
for i := 0 to Description.Height - 1 do
for j := 0 to Description.Width - 1 do
begin
with AData^ do
begin
Red := Round(Glow + Red * GlowColorMultiplier);
Green := Round(Glow + Green * GlowColorMultiplier);
Blue := Round(Glow + Blue * GlowColorMultiplier);
end;
inc(AData);
end;
end;
gdeShadowed:
begin
for i := 0 to Description.Height - 1 do
for j := 0 to Description.Width - 1 do
begin
with AData^ do
begin
Red := Round(Red * ShadowColorMultiplier);
Green := Round(Green * ShadowColorMultiplier);
Blue := Round(Blue * ShadowColorMultiplier);
end;
inc(AData);
end;
end;
gde1Bit:
begin
for i := 0 to Description.Height - 1 do
for j := 0 to Description.Width - 1 do
begin
with AData^ do
begin
// color should be either black or none
Alpha := ord
(
((R_Weight * Red + G_Weight * Green + B_Weight * Blue) < H_Threshold * 1000) and
(Alpha >= $80)
) * $FF;
if Alpha = $FF then
begin
Red := 00;
Green := 00;
Blue := 00;
end;
end;
inc(AData);
end;
end;
end;
if FreeOldData then
ReAllocMem(Data,0);
Data := P;
end;
{ TRawImageLineStarts }
function TRawImageLineStarts.GetPosition(x, y: cardinal): TRawImagePosition;
var
BitOffset: Cardinal;
begin
if FLineOrder = riloBottomToTop then
y := FHeight - y - 1;
Result := Positions[y];
BitOffset := x * FBitsPerPixel + Result.Bit;
Result.Bit := BitOffset and 7;
Inc(Result.Byte, BitOffset shr 3);
end;
procedure TRawImageLineStarts.Init(AWidth, AHeight: cardinal; ABitsPerPixel: Byte; ALineEnd: TRawImageLineEnd; ALineOrder: TRawImageLineOrder);
var
PixelCount: cardinal;
BitsPerLine: cardinal;
CurLine: cardinal;
BytesPerLine: cardinal;
ExtraBitsPerLine: Byte;
CurBitOffset: Byte;
LoopBit: Byte;
LoopByte: PtrUInt;
begin
FWidth := AWidth;
FHeight := AHeight;
FBitsPerPixel := ABitsPerPixel;
FLineEnd := ALineEnd;
FLineOrder := ALineOrder;
// get current size
PixelCount := AWidth * AHeight;
if PixelCount = 0 then exit;
// calculate BitsPerLine, BytesPerLine and ExtraBitsPerLine
BitsPerLine := GetBitsPerLine(AWidth, ABitsPerPixel, ALineEnd);
BytesPerLine := BitsPerLine shr 3;
ExtraBitsPerLine := BitsPerLine and 7;
// create line start array
SetLength(Positions, AHeight);
Positions[0].Byte := 0;
Positions[0].Bit := 0;
LoopBit := 0;
LoopByte := 0;
for CurLine := 1 to AHeight-1 do
begin
CurBitOffset := LoopBit + ExtraBitsPerLine;
LoopByte := LoopByte + BytesPerLine + (CurBitOffset shr 3);
LoopBit := CurBitOffset and 7;
Positions[CurLine].Byte := LoopByte;
Positions[CurLine].Bit := LoopBit;
end;
end;
//------------------------------------------------------------------------------
procedure InternalInit;
var
Prec: Integer;
HighValue: word;
Bits: word;
CurShift, DShift: Integer;
begin
for Prec := 0 to 15 do
begin
for HighValue := 0 to 7 do
begin
// HighValue represents the three highest bits
// For example:
// Prec=5 and the read value is %10110
// => HighValue=%101
// copy the HighValue till all missing bits are set
// For example:
// Prec=5, HighValue=%110
// => MissingBits[5,6]:=%0000011011011011
// because 00000 110 110 110 11
MissingBits[Prec, HighValue] := 0;
if Prec = 0 then Continue;
if Prec>=3 then begin
DShift := 3;
Bits := HighValue;
end else begin
DShift := Prec;
Bits := HighValue shr (3-Prec);
end;
CurShift := 16 - Prec;
while CurShift > 0 do
begin
//DebugLn(['InternalInit CurShift=',CurShift,' DShift=',DShift]);
if CurShift >= DShift then
MissingBits[Prec, HighValue] :=
word(MissingBits[Prec, HighValue] or (Bits shl (CurShift - DShift)))
else
MissingBits[Prec, HighValue] :=
word(MissingBits[Prec, HighValue] or (Bits shr (DShift - CurShift)));
Dec(CurShift, DShift);
end;
end;
end;
end;
initialization
InternalInit;
end.