• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Delphi图像处理 -- 真彩色图像转换为低色彩图像

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

特点:

1、真彩色图像转换为单色、16色、256色及16位(555和565)彩色图像;

2、可选的抖动仿色功能,使图像转换质量得到了很大提高;

3、转换256色索引图像时,使用16位映射表匹配调色板(可选),使转换速度得到了大幅度提高;

4、采用八叉树节点获取索引图像调色板时,使用了图像像素样本进行计算(可选),不仅提高了处理速度,而且也可使图像中的主色调(俗称流行色)更有机会进入调色板;

5、对索引图像,可输入任意的外部调色板进行匹配处理;

6、32位图像转索引图像时,可设置背景色,对原Alpha通道进行背景色填充。注意:在使用映射表匹配调色板时,颜色有可能发生微小变换,此时可取消映射表匹配调色板功能,好在此类图片一般不是很大,对处理速度影响不是很大。

下面是真彩色图像转低色彩图像类的全部代码:

unit IndexImage; (***************************************************************************** * * * TIndexImage: 真彩色图像转换为低色彩图像。 * * * * TIndexBitmap: TBitmap真彩色图像转换为低色彩图像。 * * * * TGpIndexBitmap: TGpBitmap真彩色图像转换为低色彩图像。 * * * * 编 制 人: 湖北省公安县统计局 毛泽发 2008.10 * * * *****************************************************************************) interface uses Windows, SysUtils, Graphics, ImageData; const MinSamplings = 1024; MaxSamplings = $7fffffff; type TIndexFormat = (if1Bit, if4Bit, if8Bit, if15Bit, if16Bit); PIndexColorArray = ^TIndexColorArray; TIndexColorArray = array[Byte] of TRGBQuad; PErrQuad = ^TErrQuad; TErrQuad = packed record Blue: SmallInt; Green: SmallInt; Red: SmallInt; Alpha: SmallInt; end; TSetDitherPixel = procedure(var Err: TErrQuad; PixelAddr: Pointer; x: Integer) of Object; PErrQuadArray = ^TErrQuadArray; TErrQuadArray = array[0..(MaxLongInt div 12) - 1] of TErrQuad; TIndexImage = class; TColorNode = class private FIsLeaf: Boolean; FPixelCount: LongWord; FRedSum: LongWord; FGreenSum: LongWord; FBlueSum: LongWord; FChild: array[0..7] of TColorNode; FNext: TColorNode; FData: TIndexImage; public constructor Create(Level: Integer; Tree: TIndexImage); destructor Destroy; override; procedure AddColor(PColor: PRGBQuad; Level: Integer); procedure GetPaletteColors(var Index: Integer); end; TIndexImage = class private FColorBits: Integer; FMaxColors: Integer; FLeafCount: Integer; FMinSamplingCount: Integer; FMonochromeThreshold: LongWord; FIndexFormat: TIndexFormat; FDithering: Boolean; FUseColorMap: Boolean; FSource: TImageData; FColorMap: PByteArray; FColors: PIndexColorArray; FNodes: array[0..8] of TColorNode; procedure CreateColorMap; procedure CreateIndexData1(var IndexData: TImageData); procedure CreateIndexData4(var IndexData: TImageData); procedure CreateIndexData8(var IndexData: TImageData); procedure CreateIndexData8_2(var IndexData: TImageData); procedure CreateIndexData15(var IndexData: TImageData); procedure CreateIndexData16(var IndexData: TImageData); procedure CreateSamplingData(Dest: TImageData); function GetColorCount: Integer; function GetColors: PIndexColorArray; function GetBitmap: HBitmap; function GetIndexColor(PColor: PRGBQuad): Integer; function GetPalette: HPalette; procedure SetBitmap(const Value: HBitmap); procedure SetDitherPixel1(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); procedure SetDitherPixel4(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); procedure SetDitherPixel8(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); procedure SetDitherPixel8_2(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); procedure SetDitherPixel15(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); procedure SetDitherPixel16(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); procedure SetFormat(const Value: TIndexFormat); procedure SetMinSamplingCount(const Value: Integer); procedure SetMonochromeThreshold(const Value: LongWord); procedure SetPalette(const Value: HPalette); procedure SetSourceData(const Value: TImageData); protected function CanIndex: Boolean; procedure CreateColors; procedure CreateDitherData(var IndexData: TImageData; SetPixelProc: TSetDitherPixel); procedure CreateIndexImageData(var IndexData: TImageData); procedure FillBackground(ColorBackground: LongWord); procedure ColorsChange; virtual; procedure ReduceTree; procedure SetColorCount(Count: Integer); property ColorBits: Integer read FColorBits; property MaxColorCount: Integer read FMaxColors; // 获取或设置数据源结构,注意:设置数据源是浅拷贝 property SourceData: TImageData read FSource write SetSourceData; public constructor Create; destructor Destroy; override; // 设置颜色表(可按给定颜色表匹配索引位图)。AColors=[]清除颜色表。 procedure SetColors(const AColors: array of TRGBQuad); // 获取按IndexFormat匹配的位图句柄,或者设置位图源句柄。不清除调色板颜色表 property Bitmap: HBitmap read GetBitmap write SetBitmap; // 获取调色板颜色的个数 property ColorCount: Integer read GetColorCount; // 获取调色板颜色表 property Colors: PIndexColorArray read GetColors; // 转换图像时是否抖动仿色,缺省为False property Dithering: Boolean read FDithering write FDithering; // 获取或设置索引图格式。设置格式将清除可能存在的调色板颜色表 property IndexFormat: TIndexFormat read FIndexFormat write SetFormat; // 转换单色图像时的阀值。缺省为128 property MonochromeThreshold: LongWord read FMonochromeThreshold write SetMonochromeThreshold; // 建立调色板的像素最小样本数(不得小于MinSamplings),缺省为4096。 // 如果像素个数/100>=最小样本数,取图像尺寸1/10为样本,否则取>=4096的图像尺寸 property MinSamplingCount: Integer read FMinSamplingCount write SetMinSamplingCount; // 获取或设置调色板(可按给定调色板匹配索引位图),Palette=0清除颜色表 property Palette: HPalette read GetPalette write SetPalette; // True: 使用16位颜色映射表匹配调色板转换8bit图像(4bit图像转换无映射表) // Flae: 直接匹配调色板,但匹配速度较慢。缺省为True。 property UseColorMap: Boolean read FUseColorMap write FUseColorMap; end; TIndexBitmap = class(TIndexImage) public // 获取按IndexFormat匹配的TBitmap对象 function GetIndexBitmap: TBitmap; // 设置源对象。ColorBackground为32位图像Alpha背景色,0不填充。不清除调色板颜色表 procedure SetSource(Bitmap: TBitmap; ColorBackground: TColor = 0); overload; procedure SetSource(Graphic: TGraphic; ColorBackground: TColor = 0); overload; end; TGpIndexBitmap = class(TIndexImage) private FPalette: PColorPalette; function GetPalette: PColorPalette; procedure SetPalette(const Value: PColorPalette); protected procedure ColorsChange; override; public // 获取按IndexFormat匹配的TGpBitmap对象 function GetIndexBitmap: TGpBitmap; // 设置源对象。ColorBackground为32位图像Alpha背景色,0不填充。不清除调色板颜色表 procedure SetSource(Bitmap: TGpBitmap; ColorBackground: TARGB = 0); // 获取或设置GDI+调色板(可按给定调色板匹配索引位图),ColorPalette=nil清除颜色表 property ColorPalette: PColorPalette read GetPalette write SetPalette; end; implementation { TColorNode } procedure TColorNode.AddColor(PColor: PRGBQuad; Level: Integer); const mask: array[0..7] of Byte = ($80, $40, $20, $10, $08, $04, $02, $01); var Index, shift: Integer; begin if FIsLeaf then begin Inc(FPixelCount); Inc(FRedSum, PColor^.rgbRed); Inc(FGreenSum, PColor^.rgbGreen); Inc(FBlueSum, PColor^.rgbBlue); end else begin shift := 7 - Level; Index := (((PColor.rgbRed and mask[Level]) shr shift) shl 2) or (((PColor.rgbGreen and mask[Level]) shr shift) shl 1) or ((PColor.rgbBlue and mask[Level]) shr shift); Inc(Level); if not Assigned(FChild[Index]) then FChild[Index] := TColorNode.Create(Level, FData); FChild[Index].AddColor(PColor, Level); end; end; constructor TColorNode.Create(Level: Integer; Tree: TIndexImage); begin FData := Tree; FIsLeaf := Level = FData.FColorBits; if FIsLeaf then Inc(FData.FLeafCount) else begin FNext := FData.FNodes[Level]; FData.FNodes[Level] := Self; end; end; destructor TColorNode.Destroy; var I: Integer; begin for I := 0 to 7 do if Assigned(FChild[I]) then FChild[I].Free; end; procedure TColorNode.GetPaletteColors(var Index: Integer); var I: Integer; begin if FIsLeaf then begin FData.FColors[Index].rgbRed := FRedSum div FPixelCount; FData.FColors[Index].rgbGreen := FGreenSum div FPixelCount; FData.FColors[Index].rgbBlue := FBlueSum div FPixelCount; FData.FColors[Index].rgbReserved := 0; Inc(Index); end else begin for I := 0 to 7 do if Assigned(FChild[I]) then FChild[I].GetPaletteColors(Index); end; end; { TIndexImage } function TIndexImage.CanIndex: Boolean; begin Result := FSource.Scan0 <> nil; end; procedure TIndexImage.ColorsChange; begin end; constructor TIndexImage.Create; begin IndexFormat := if8bit; FMinSamplingCount := 4096; FMonochromeThreshold := 128; FUseColorMap := True; end; // 建立调色板颜色表 procedure TIndexImage.CreateColors; var Node: TColorNode; x, y, Index: Integer; Offset: Integer; P: PRGBQuad; Scale: Single; tmp: TImageData; begin if not CanIndex or (FLeafCount > 0) or (IndexFormat > if8Bit) then Exit; if IndexFormat = if1Bit then begin SetColorCount(2); LongWord(FColors[0]) := 0; LongWord(FColors[1]) := $FFFFFF; Exit; end; Node := TColorNode.Create(0, Self); try if (FSource.Width * FSource.Height div 100) >= FMinSamplingCount then Scale := 0.1 else begin Scale := 0.9; while (Trunc(FSource.Width * Scale * FSource.Height * Scale)) > FMinSamplingCount do Scale := Scale - 0.1; Scale := Scale + 0.1; end; if Scale < 1.0 then // 如果Source像素数量>MinSamplingCount,取样本数据 begin tmp := GetImageData(Round(Scale * FSource.Width), Round(Scale * FSource.Height)); CreateSamplingData(tmp); end else // 否则取原图像数据 tmp := GetImageData(FSource.Width, FSource.Height, FSource.Stride, FSource.Scan0); try P := tmp.Scan0; // 建立颜色八叉树 Offset := tmp.Stride - (tmp.Width shl 2); for y := 1 to tmp.Height do begin for x := 1 to tmp.Width do begin Node.AddColor(P, 0); while FLeafCount > FMaxColors do ReduceTree; Inc(P); end; Inc(Integer(P), Offset); end; finally FreeImageData(tmp); end; SetColorCount(FLeafCount); Index := 0; Node.GetPaletteColors(Index); // 获取调色板颜色表 finally Node.Free; end; end; // 根据调色板颜色表建立8位索引图像的16位颜色映射表 procedure TIndexImage.CreateColorMap; var r, g, b: Integer; Color: TRGBQuad; begin r := 32 * 32 * 32; GetMem(FColorMap, r); while r > 0 do begin Dec(r, 32 * 32); Color.rgbRed := r shr 7; g := 32 * 32; while g > 0 do begin Dec(g, 32); Color.rgbGreen := g shr 2; b := 32; while b > 0 do begin Dec(b); Color.rgbBlue := b shl 3; FColorMap[r or g or b] := GetIndexColor(@Color); end; end; end; end; procedure TIndexImage.CreateDitherData(var IndexData: TImageData; SetPixelProc: TSetDitherPixel); var PErrs, Errs0, Errs1: PErrQuadArray; Width: Integer; srcOffset, dstStride: Integer; this, dstScan0: Pointer; procedure FillErrs; asm push edi push ecx mov edi, Errs1 mov ecx, Width add ecx, ecx xor eax, eax rep stosd pop ecx pop edi end; asm push esi push edi push ebx mov this, eax push edx mov eax, [edx].TImageData.Stride mov dstStride, eax mov edx, [edx].TImageData.Width add edx, 2 shl edx, 3 + 1 // edx = (Dest.Width + 2) * Sizeof(TErrQuad) * 2 push edx mov eax, GMEM_MOVEABLE call GlobalAllocPtr mov PErrs, eax // PErrs = GlobalAllocPtr(GMEM_MOVEABLE, edx) add eax, 8 mov Errs0, eax // Errs0 = @PErrs[1] pop edx shr edx, 1 add eax, edx mov Errs1, eax // Errs1 = @PErrs[Dest.Width + 2 + 1] pop edx mov eax, this lea eax, [eax].TIndexImage.FSource call SetCopyRegs mov srcOffset, ebx mov dstScan0, esi mov esi, edi // esi = Source.Scan0 mov Width, ecx pxor mm7, mm7 mov ebx, 70007h movd mm6, ebx punpcklwd mm6, mm6 mov ebx, 50005h movd mm5, ebx punpcklwd mm5, mm5 mov ebx, 30003h movd mm4, ebx punpcklwd mm4, mm4 call FillErrs // FillErrs(Errs1, Dest.Width, 0) @yLoop: // for (y = 0; y < Height; y ++) mov ebx, Errs0 // { mov edi, Errs1 mov ecx, Width @readLoop: // for (x = 0; x < Width; x ++) movd mm0, [esi] // { punpcklbw mm0, mm7 // Errs0[x] = Errs1[x] + Source.Pixels[x, y] paddw mm0, [edi] // if (Errs0[x] < 0) packuswb mm0, mm7 // Errs[x] = 0 punpcklbw mm0, mm7 // else if (Errs0[x] > 255) movq [ebx], mm0 // Errs0[x] = 255 add esi, 4 // add edi, 8 add ebx, 8 loop @readLoop // } call FillErrs // FillErrs(Errs1, Dest.Width, 0) xor ecx, ecx push edx mov ebx, Errs0 mov edi, Errs1 @xLoop: // for (x = 0; x < Width; x ++) movq mm1, [ebx] // { push ecx // mm1 = Errs0[x] push ecx mov edx, ebx mov ecx, dstScan0 mov eax, this // SetPixelProc(Errs0[x], Dest.Scan0, x) call dword ptr SetPixelProc pop ecx // mm1 = currErr = SetPixelProc return value add ebx, 8 movq mm0, mm1 // Right: pmullw mm0, mm6 // Errs0[x+1] += (currErr * 7/16) psraw mm0, 4 // if (Errs0[x] < 0) Errs0[x] = 0 paddsw mm0, [ebx] // if (Errs0[x] > 255) Errs0[x] = 255 packuswb mm0, mm7 punpcklbw mm0, mm7 movq [ebx], mm0 movq mm0, mm1 // Bottom: pmullw mm0, mm5 // Errs1[x] += (currErr * 5/16) psraw mm0, 4 // if (Errs1[x] < 0) Errs1[x] = 0 paddsw mm0, [edi] // if (Errs1[x] > 255) Errs1[x] = 255 packuswb mm0, mm7 punpcklbw mm0, mm7 movq [edi], mm0 sub edi, 8 movq mm0, mm1 // Left Bottom: pmullw mm0, mm4 // Errs1[x-1] += (currErr * 3/16) psraw mm0, 4 // if (Errs1[x-1] < 0) Errs1[x-1] = 0 paddsw mm0, [edi] // if (Errs1[x-1] > 255) Errs1[x-1] = 255 packuswb mm0, mm7 punpcklbw mm0, mm7 movq [edi], mm0 add edi, 16 // Right Bottom: psraw mm1, 4 // Errs1[x+1] += (currErr * 1/16) paddsw mm1, [edi] // if (Errs1[x+1] < 0) Errs1[x+1] = 0 packuswb mm1, mm7 // if (Errs1[x+1] > 255) Errs1[x+1] = 255 punpcklbw mm1, mm7 movq [edi], mm1 inc ecx cmp ecx, Width jl @xLoop // } pop edx add esi, srcOffset // esi += srcOffset mov eax, dstStride add dstScan0, eax // dstScan0 += Dest.Stride dec edx jnz @yLoop // } emms mov eax, PErrs call GlobalFreePtr // GlobalFreePtr(PErrs) pop ebx pop edi pop esi end; procedure TIndexImage.CreateIndexData1(var IndexData: TImageData); var dstOffset, srcOffset, Height: Integer; procedure CopyPixel; asm mov eax, 8000h @PixelLoop: cmp [esi], bl jb @@1 or al, ah @@1: shr ah, 1 add esi, 4 loop @PixelLoop stosb end; asm push esi push edi push ebx push [eax].TIndexImage.FMonochromeThreshold lea eax, [eax].TIndexImage.FSource xchg eax, edx call SetCopyRegs mov dstOffset, ebx mov srcOffset, eax mov Height, edx mov edx, 7 and edx, ecx shr ecx, 3 pop ebx @yLoop: push ecx jecxz @@1 @xLoop: push ecx mov ecx, 8 call CopyPixel pop ecx loop @xLoop @@1: mov ecx, edx jecxz @@2 call CopyPixel @@2: pop ecx add esi, srcOffset add edi, dstOffset dec Height jnz @yLoop pop ebx pop edi pop esi end; procedure TIndexImage.CreateIndexData4(var IndexData: TImageData); var srcOffset, dstOffset, Width, Height: Integer; asm push esi push edi push ebx push eax lea eax, [eax].TIndexImage.FSource xchg eax, edx call SetCopyRegs mov srcOffset, eax mov dstOffset, ebx mov Height, edx mov Width, ecx shr ecx, 1 pop ebx @yLoop: push ecx jecxz @@1 @xLoop: push ecx mov edx, esi mov eax, ebx call GetIndexColor shl eax, 4 add esi, 4 push eax mov edx, esi mov eax, ebx call GetIndexColor pop ecx or eax, ecx stosb add esi, 4 pop ecx loop @xLoop @@1: test Width, 1 jz @@2 mov edx, esi mov eax, ebx call GetIndexColor shl eax, 4 stosb add esi, 4 @@2: pop ecx add esi, srcOffset add edi, dstOffset dec Height jnz @yLoop pop ebx pop edi pop esi end; procedure TIndexImage.CreateIndexData8(var IndexData: TImageData); var srcOffset, dstOffset, Height: Integer; asm push esi push edi push ebx push eax lea eax, [eax].TIndexImage.FSource xchg eax, edx call SetCopyRegs mov srcOffset, eax mov dstOffset, ebx mov Height, edx pop eax mov ebx, [eax].TIndexImage.FColorMap @yLoop: push ecx @xLoop: lodsd mov edx, eax shr dh, 3 shr dx, 3 shr eax, 9 and eax, 7c00h or ax, dx mov al, [ebx + eax] stosb loop @xLoop pop ecx add esi, srcOffset add edi, dstOffset dec Height jnz @yLoop pop ebx pop edi pop esi end; procedure TIndexImage.CreateIndexData8_2(var IndexData: TImageData); var srcOffset, dstOffset, Height: Integer; asm push esi push edi push ebx push eax lea eax, [eax].TIndexImage.FSource xchg eax, edx call SetCopyRegs mov srcOffset, eax mov dstOffset, ebx mov Height, edx pop ebx @yLoop: push ecx @xLoop: push ecx mov edx, esi mov eax, ebx call GetIndexColor stosb pop ecx add esi, 4 loop @xLoop pop ecx add esi, srcOffset add edi, dstOffset dec Height jnz @yLoop pop ebx pop edi pop esi end; procedure TIndexImage.CreateIndexData15(var IndexData: TImageData); var dstOffset, srcoffset: Integer; asm push esi push edi push ebx lea eax, [eax].TIndexImage.FSource xchg eax, edx call SetCopyRegs mov dstOffset, ebx mov srcOffset, eax @yLoop: push ecx @xLoop: lodsd mov ebx, eax shr bh, 3 shr bx, 3 shr eax, 9 and ax, 7c00h or ax, bx stosw loop @xLoop pop ecx add esi, srcOffset add edi, dstOffset dec edx jnz @yLoop pop ebx pop edi pop esi end; procedure TIndexImage.CreateIndexData16(var IndexData: TImageData); var dstOffset, srcoffset: Integer; asm push esi push edi push ebx lea eax, [eax].TIndexImage.FSource xchg eax, edx call SetCopyRegs mov dstOffset, ebx mov srcOffset, eax @yLoop: push ecx @xLoop: lodsd mov ebx, eax shr bh, 2 shr bx, 3 shr eax, 8 and ax, 0f800h or ax, bx stosw loop @xLoop pop ecx add esi, srcOffset add edi, dstOffset dec edx jnz @yLoop pop ebx pop edi pop esi end; // 获取索引位图 procedure TIndexImage.CreateIndexImageData(var IndexData: TImageData); procedure GrayCopy(Dest, Source: TImageData); var srcOffset: Integer; asm push esi push edi push ebx call SetCopyRegs mov srcOffset, eax @yLoop: push ecx @xLoop: movzx eax, [esi] movzx ebx, [esi + 1] imul eax, 117 // blue 0.114 * 1024 imul ebx, 601 // green 0.587 * 1024 add eax, ebx movzx ebx, [esi + 2] imul ebx, 306 // red 0.299 * 1024 add eax, ebx add eax, 512 // Rounding shr eax, 10 // eax = Round((R * 306 + G * 601 + B * 117) / 1024) mov [edi], al // blue = al add edi, 4 add esi, 4 loop @xLoop pop ecx add esi, srcOffset dec edx jnz @yLoop pop ebx pop edi pop esi end; var saveData: TImageData; begin if not CanIndex then // 数据源不存在,错误 raise EImageDataError.Create(EIDErrorEmptyData); CreateColors; // 建立颜色表 case IndexFormat of if1Bit: // 单色 begin saveData := FSource; // 保存源图像数据 FSource := GetImageData(saveData.Width, saveData.Height); try GrayCopy(FSource, saveData);// 拷贝灰度图像 if FDithering then CreateDitherData(IndexData, SetDitherPixel1) else CreateIndexData1(IndexData); finally FreeImageData(FSource); FSource := saveData; // 恢复源图像数据 end; end; if4Bit: // 16色 if FDithering then CreateDitherData(IndexData, SetDitherPixel4) else CreateIndexData4(IndexData); if8Bit: // 256色 begin if UseColorMap then // 使用映射表匹配调色板 begin CreateColorMap; if FDithering then CreateDitherData(IndexData, SetDitherPixel8) else CreateIndexData8(IndexData); FreeMem(FColorMap); end else // 直接匹配调色板 begin if FDithering then CreateDitherData(IndexData, SetDitherPixel8_2) else CreateIndexData8_2(IndexData); end; end; if15Bit: // 15位(555) if FDithering then CreateDitherData(IndexData, SetDitherPixel15) else CreateIndexData15(IndexData); if16Bit: if FDithering then // 16位(565) CreateDitherData(IndexData, SetDitherPixel16) else CreateIndexData16(IndexData); end; end; // 获取样本图像数据 procedure TIndexImage.CreateSamplingData(Dest: TImageData); var xDelta, yDelta: Integer; asm push esi push edi push ebx mov edi, edx lea ebx, [eax].TIndexImage.FSource mov eax, [ebx].TimageData.Height shl eax, 8 cdq div [edi].TimageData.Height mov yDelta, eax // yDelta = Source.Height * 256 / Dest.Height mov ecx, [edi].TimageData.Height dec ecx imul ecx, eax // y = (Dest.Height - 1) * yDelta mov eax, [ebx].TimageData.Width shl eax, 8 cdq div [edi].TimageData.Width mov xDelta, eax // xDelta = Source.Width * 256 / Dest.Width mov edx, [edi].TimageData.Width dec edx imul edx, eax // x = (Dest.Width - 1) * xDelta mov edi, [edi].TImageData.Scan0 // edi = Dest.Scan0 @yLoop: // for (; y >= 0; y -= yDelta) mov esi, ecx // { shr esi, 8 // esi = Source.Scan0 + y / 256 * Source.Stride imul esi, [ebx].TImageData.Stride add esi, [ebx].TImageData.Scan0 push edx @xLoop: push esi // for (; x >= 0; x -= xDelta) mov eax, edx // { shr eax, 8 shl eax, 2 add esi, eax // esi += (x / 256 * 4) movsd // *edi ++ = *esi pop esi sub edx, xDelta jns @xLoop // } pop edx sub ecx, yDelta jns @yLoop // } pop ebx pop edi pop esi end; destructor TIndexImage.Destroy; begin SetColorCount(0); FreeImageData(FSource); end; // 填充Alpha通道背景颜色 procedure TIndexImage.FillBackground(ColorBackground: LongWord); asm push edi push ebx or edx, 0ff000000h pxor mm7, mm7 // mm7 = 00 00 00 00 00 00 00 00 movd mm3, edx // mm3 = 00 00 00 00 Ad Rd Gd Bd punpcklbw mm3, mm7 // mm3 = 00 Ad 00 Rd 00 Gd 00 Bd movq mm1, mm3 psllw mm1, 8 // mm1 = Ad*256 Rd*256 Gd*256 Bd*256 lea edi, [eax].TIndexImage.FSource mov ecx, [edi].TImageData.Width mov edx, [edi].TImageData.Height mov ebx, [edi].TImageData.Stride mov edi, [edi].TImageData.Scan0 mov eax, ecx shl eax, 2 sub ebx, eax cld @yLoop: push ecx @xLoop: movd mm0, [edi] // mm0 = 00 00 00 00 As Rs Gs Bs punpcklbw mm0, mm7 // mm0 = 00 As 00 Rs 00 Gs 00 Bs movq mm2, mm0 punpckhwd mm2, mm2 punpckhdq mm2, mm2 // mm2 = Alpha Alpha Alpha Alpha psubw mm0, mm3 // mm0 = As-Ad Rs-Rd Gs-Gd Bs-Bd pmullw mm0, mm2 // mm0 = As*Alpha Rs*Alpha Gs*Alpha Bs*Alpha paddw mm0, mm1 // mm0 = 00 An 00 Rn 00 Gn 00 Bn psrlw mm0, 8 // mm0 = An/256 Rn/256 Gn/256 Bn/256 packuswb mm0, mm7 // mm0 = 00 00 00 00 An Rn Gn Bn movd [edi], mm0 add edi, 4 loop @xLoop pop ecx add edi, ebx dec edx jnz @yLoop emms pop ebx pop edi end; // 获取Windows位图句柄 function TIndexImage.GetBitmap: HBitmap; const BitFields: array[0..2] of LongWord = ($F800, $07E0, $001F); var pbi: PBitmapInfo; Data: TImageData; DC: HDC; begin Result := 0; if not CanIndex then Exit; Data := GetImageData(FSource.Width, FSource.Height, TPixelFormat(Integer(IndexFormat) + 1)); Data.InvertLine := True; DC := GetDC(0); try CreateIndexImageData(Data); GetMem(pbi, Sizeof(TBitmapInfoHeader) + 12 + Sizeof(TRGBQuad) * FLeafCount); try pbi^.bmiHeader := GetBitmapInfoHeader(Data); if Data.PixelFormat and $ffff = $1000 then begin pbi^.bmiHeader.biCompression := BI_BITFIELDS; Move(BitFields, pbi^.bmiColors, Sizeof(TRGBQuad) * 3); end else Move(FColors^, pbi^.bmiColors, Sizeof(TRGBQuad) * FLeafCount); Result := CreateDIBitmap(DC, pbi^.bmiHeader, CBM_INIT, Data.Scan0, pbi^, DIB_RGB_COLORS); finally FreeMem(pbi); end; finally ReleaseDC(0, DC); FreeImageData(Data); end; end; function TIndexImage.GetColorCount: Integer; begin if FLeafCount = 0 then CreateColors; Result := FLeafCount; end; function TIndexImage.GetColors: PIndexColorArray; begin CreateColors; Result := FColors; end; // 按颜色表平方差匹配像素颜色 function TIndexImage.GetIndexColor(PColor: PRGBQuad): Integer; var Count, Index, Diff: LongWord; asm push esi push edi push ebx mov ecx, [eax].TIndexImage.FLeafCount mov Count, ecx mov esi, [eax].TIndexImage.FColors movzx ebx, [edx] movzx ecx, [edx + 1] movzx edi, [edx + 2] mov Diff, 7FFFFFFFh mov Index, esi push esi @Loop: movzx eax, [esi] movzx edx, [esi + 1] sub eax, ebx sub edx, ecx imul eax, eax imul edx, edx add eax, edx movzx edx, [esi + 2] sub edx, edi imul edx, edx add eax, edx jnz @@4 mov Index, esi jmp @@6 @@4: cmp eax, Diff jae @@5 mov Diff, eax mov Index, esi @@5: add esi, 4 dec Count jnz @Loop @@6: pop esi mov eax, Index sub eax, esi shr eax, 2 pop ebx pop edi pop esi end; // 获取Windows位图调色板句柄 function TIndexImage.GetPalette: HPalette; var Pal: TMaxLogPalette; begin Result := 0; CreateColors; if FLeafCount = 0 then Exit; Move(FColors^, Pal.palPalEntry, Sizeof(TRGBQuad) * FLeafCount); SwapColors(@Pal.palPalEntry, FLeafCount); Pal.palVersion := $300; Pal.palNumEntries := FLeafCount; Result := CreatePalette(PLogPalette(@Pal)^); end; // 归并八叉树 procedure TIndexImage.ReduceTree; var I: Integer; Node: TColorNode; begin I := FColorBits - 1; while FNodes[I] = nil do Dec(I); Node := FNodes[I]; FNodes[I] := Node.FNext; for I := 0 to 7 do begin if Node.FChild[I] <> nil then begin Inc(Node.FRedSum, Node.FChild[I].FRedSum); Inc(Node.FGreenSum, Node.FChild[I].FGreenSum); Inc(Node.FBlueSum, Node.FChild[I].FBlueSum); Inc(Node.FPixelCount, Node.FChild[I].FPixelCount); FreeAndNil(Node.FChild[I]); Dec(FLeafCount); end; end; Inc(FLeafCount); Node.FIsLeaf := True; end; // 通过Windows位图调色板句柄获取源图像数据结构 procedure TIndexImage.SetBitmap(const Value: HBitmap); var DC: HDC; bmi: TBitmapInfo; begin FreeImageData(FSource); if Value = 0 then Exit; DC := GetDC(0); try FSource.PixelFormat := 0; bmi.bmiHeader := GetBitmapInfoHeader(FSource); if GetDIBits(DC, Value, 0, 1, nil, bmi, DIB_RGB_COLORS) = 0 then raise EImageDataError.Create(EIDErrorParam); FSource := GetImageData(bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, pf32bit); FSource.InvertLine := True; bmi.bmiHeader := GetBitmapInfoHeader(FSource); GetDIBits(DC, Value, 0, FSource.Height, FSource.Scan0, bmi, DIB_RGB_COLORS); finally ReleaseDC(0, DC); end; end; procedure TIndexImage.SetColorCount(Count: Integer); begin if FColors <> nil then begin FreeMem(FColors); FColors := nil; end; FLeafCount := Count; if FLeafCount > 0 then GetMem(FColors, Sizeof(TRGBQuad) * FLeafCount); ColorsChange; end; // 设置外部颜色表 procedure TIndexImage.SetColors(const AColors: array of TRGBQuad); begin if IndexFormat > if8Bit then Exit; SetColorCount(Length(AColors)); if FLeafCount = 0 then Exit; Move(AColors, FColors^, Sizeof(TRGBQuad) * FLeafCount); end; // in mm1 = Errs[x]; out mm1 = currErr procedure TIndexImage.SetDitherPixel1(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); const mask1: int64 = $00ff00ff00ff; asm push edi mov edi, x shr edi, 3 // edi = PixelAddr + x / 8 add edi, ecx mov eax, [eax].TIndexImage.FMonochromeThreshold test x, 7 jnz @@1 mov [edi], ah // if (x & 7 == 0) *edi = 0 (init) @@1: cmp ax, [edx].TErrQuad.Blue jae @@2 mov eax, 80h // if (Err > Threshold) mov ecx, 7 // { and ecx, x // *edi |= (0x80 >> (x & 7)) shr eax, cl // currErr = Errs[x] - 255 or [edi], al // } psubsw mm1, mask1 @@2: pop edi end; // in mm1 = Errs[x]; out mm1 = currErr procedure TIndexImage.SetDitherPixel4(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); asm push [eax].TIndexImage.FColors push ecx movq mm0, mm1 packuswb mm0, mm7 movq [edx], mm0 call GetIndexColor // index = GetIndexColor(@Errs[x]) pop ecx mov edx, x shr edx, 1 add edx, ecx // edx = PixelAddr + x / 2 test x, 1 jnz @@1 shl eax, 4 mov [edx], al // if (x & 1 == 0) *edx = index << 4 shr eax, 4 jmp @@2 @@1: or [edx], al // else *edx |= index @@2: pop ecx shl eax, 2 add ecx, eax movd mm0, [ecx] punpcklbw mm0, mm7 psubsw mm1, mm0 // currErr = Errs[x] - FColors[index] end; // in mm1 = Errs[x]; out mm1 = currErr procedure TIndexImage.SetDitherPixel8(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); asm add ecx, x push ecx mov ecx, [edx] mov dx, [edx].TErrQuad.Red and edx, 0f8h and ecx, 0f800f8h shl edx, 7 shr cx, 3 or dx, cx shr ecx, 14 or edx, ecx add edx, [eax].TIndexImage.FColorMap movzx edx, [edx] // index = FColorMap[Errs[x] to 16Bit] (555) pop ecx // ecx = PixelAddr + x mov [ecx], dl // *ecx = index shl edx, 2 add edx, [eax].TIndexImage.FColors movd mm0, [edx] punpcklbw mm0, mm7 psubsw mm1, mm0 // currErr = Errs[x] - FColors[index] end; // in mm1 = Errs[x]; out mm1 = currErr procedure TIndexImage.SetDitherPixel8_2(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); asm push [eax].TIndexImage.FColors push ecx movq mm0, mm1 packuswb mm0, mm7 movq [edx], mm0 call GetIndexColor // index = GetIndexColor(@Errs[x]) pop ecx add ecx, x mov [ecx], al // *ecx = index pop edx shl eax, 2 movd mm0, [edx + eax] punpcklbw mm0, mm7 psubsw mm1, mm0 // currErr = Errs[x] - FColors[index] end; // in mm1 = Errs[x]; out mm1 = currErr procedure TIndexImage.SetDitherPixel15(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); const mask16: int64 = $000700070007; asm pand mm1, mask16 // currErr = Errs[x] & 0x0700030007 mov eax, [edx] mov dx, [edx].TErrQuad.Red and edx, 0f8h and eax, 0f800f8h shl edx, 7 shr ax, 3 or dx, ax shr eax, 14 or eax, edx add ecx, x add ecx, x // ecx = PixelAddr + x * 2 mov [ecx], ax // *ecx = Errs[x] to 16Bit (555) end; // in mm1 = Errs[x]; out mm1 = currErr procedure TIndexImage.SetDitherPixel16(var Err: TErrQuad; PixelAddr: Pointer; x: Integer); const mask16: int64 = $000700030007; asm pand mm1, mask16 // currErr = Errs[x] & 0x0700030007 mov eax, [edx] mov dx, [edx].TErrQuad.Red and edx, 0f8h and eax, 0fc00f8h shl edx, 8 shr ax, 3 or dx, ax shr eax, 13 or eax, edx add ecx, x add ecx, x // ecx = PixelAddr + x * 2 mov [ecx], ax // *ecx = Errs[x] to 16Bit (565) end; procedure TIndexImage.SetFormat(const Value: TIndexFormat); const Bits: array[TIndexFormat] of Integer = (1, 4, 8, 16, 16); begin if IndexFormat <> Value then begin FIndexFormat := Value; FColorBits := Bits[Value]; FMaxColors := 1 shl FColorBits; SetColorCount(0); end; end; // 设置图像样本图的像素个数 procedure TIndexImage.SetMinSamplingCount(const Value: Integer); begin if FMinSamplingCount <> Value then begin FMinSamplingCount := Value; if FMinSamplingCount < MinSamplings then FMinSamplingCount := MinSamplings; SetColorCount(0); end; end; procedure TIndexImage.SetMonochromeThreshold(const Value: LongWord); begin if Value < 256 then FMonochromeThreshold := Value; end; // 通过外部Windows调色板句柄获取颜色表 procedure TIndexImage.SetPalette(const Value: HPalette); var Pal: TMaxLogPalette; begin if Value = 0 then SetColorCount(0) else if IndexFormat <= if8Bit then begin SetColorCount(GetPaletteEntries(Value, 0, FMaxColors, Pal.palPalEntry)); if FLeafCount > 0 then begin SwapColors(@Pal.palPalEntry, FLeafCount); Move(Pal.palPalEntry, FColors^, Sizeof(TRGBQuad) * FLeafCount); end; end; end; // 设置源图像数据结构(浅拷贝) procedure TIndexImage.SetSourceData(const Value: TImageData); begin if (Value.PixelFormat shr 8) and $ff <> 32 then raise EImageDataError.CreateFmt(EIDErrorSourceMust, [32]); FreeImageData(FSource); FSource := Value; end; { TIndexBitmap } function TIndexBitmap.GetIndexBitmap: TBitmap; begin if CanIndex then begin Result := TBitmap.Create; Result.Handle := GetBitmap; end else Result := nil; end; procedure TIndexBitmap.SetSource(Bitmap: TBitmap; ColorBackground: TColor); begin SetBitmap(Bitmap.Handle); if (Bitmap.PixelFormat = pf32Bit) and (ColorBackground <> 0) then begin SwapColors(@ColorBackground, 1); FillBackground(ColorBackground); end; end; procedure TIndexBitmap.SetSource(Graphic: TGraphic; ColorBackground: TColor); var tmp: TBitmap; begin if Graphic is TBitmap then SetSource(TBitmap(Graphic)) else begin tmp := TBitmap.Create; try tmp.Assign(Graphic); SetSource(tmp); finally tmp.Free; end; end; end; { TGpIndexBitmap } procedure TGpIndexBitmap.ColorsChange; begin if FPalette <> nil then begin FreeMem(FPalette); FPalette := nil; end; end; function TGpIndexBitmap.GetIndexBitmap: TGpBitmap; const Formats: array[TIndexFormat] of Gdiplus.TPixelFormat = (pf1bppIndexed, pf4bppIndexed, pf8bppIndexed, pf16bppRGB555, pf16bppRGB565); var Data: TBitmapData; begin if CanIndex then begin Result := TGpBitmap.Create(SourceData.Width, SourceData.Height, Formats[IndexFormat]); if (IndexFormat <= if8bit) then Result.Palette := GetPalette; Data := Result.LockBits(GpRect(0, 0, Result.Width, Result.Height), [imRead, imWrite], Result.PixelFormat); try CreateIndexImageData(TImageData(Data)); finally Result.UnlockBits(Data); end; end else Result := nil; end; function TGpIndexBitmap.GetPalette: PColorPalette; var I: Integer; cols: PIndexColorArray; begin if ColorCount > 0 then begin GetMem(FPalette, Sizeof(TColorPalette) + Sizeof(TARGB) * (ColorCount - 1)); FPalette^.Flags := 0; FPalette^.Count := ColorCount; cols := Colors; for I := 0 to FPalette^.Count - 1 do FPalette^.Entries[I] := TARGB(cols[I]) or $ff000000; end; Result := FPalette; end; procedure TGpIndexBitmap.SetPalette(const Value: PColorPalette); var Count: Integer; cols: PIndexColorArray; begin if IndexFormat > if8Bit then Exit; Count := 0; if Value <> nil then begin Count := Value.Count; if Count > MaxColorCount then Count := MaxColorCount; end; SetColorCount(Count); if Count > 0 then begin cols := Colors; Move(Value.Entries, cols^, Count * Sizeof(TARGB)); end; end; procedure TGpIndexBitmap.SetSource(Bitmap: TGpBitmap; ColorBackground: TARGB); var Data: TBitmapData; begin TImageData(Data) := GetImageData(Bitmap.Width, Bitmap.Height); Data := Bitmap.LockBits(GpRect(0, 0, Integer(Data.Width), Integer(Data.Height)), [imRead, imUserInputBuf], pf32bppARGB); Bitmap.UnlockBits(Data); SourceData := TImageData(Data); if (Bitmap.PixelFormat = pf32bppARGB) and (ColorBackground <> 0) then FillBackground(ColorBackground); end; end.

TBitmap应用举例:

var Bmp, Bmp8: TBitmap; IdxData: TIndexBitmap; begin Bmp := TBitmap.Create; Bmp.LoadFromFile('D:/VclLib/GdiplusDemo/Media/20041001.bmp'); IdxData := TIndexBitmap.Create; IdxData.SetSource(Bmp); IdxData.Dithering := True; IdxData.IndexFormat := if8bit; // 转换为256色 Bmp8 := IdxData.GetIndexBitmap; Canvas.Draw(0, 0, Bmp); Canvas.Draw(0, Bmp.Height, Bmp8); Bmp8.Free; IdxData.Free; Bmp.Free; end;

GDI+应用举例:

var Bmp, Bmp8: TGpBitmap; IdxData: TGpIndexBitmap; g: TGpGraphics; begin Bmp := TGpBitmap.Create('D:/56-3.jpg'); IdxData := TGpIndexBitmap.Create; IdxData.SetSource(Bmp); IdxData.Dithering := True; IdxData.IndexFormat := if1bit; // 转换为单色 Bmp8 := IdxData.GetIndexBitmap; g := TGpGraphics.Create(Canvas.Handle); g.DrawImage(Bmp, 0, 0); g.DrawImage(Bmp8, 0, Bmp.Height); g.Free; Bmp8.Free; IdxData.Free; Bmp.Free;

下面是本类转换的几张图片效果,从上到下,依次为:源图,单色图,单色仿色图,16色图,16色仿色图,256色图,256色仿色图:

源图:

单色图:

单色仿色图:

16色图:

16色仿色图:

256色图:

256色仿色图:

例子使用GDI+版本下载地址和说明见《GDI+ for VCL基础 -- GDI+ 与 VCL》。

文章中所用数据类型及某些过程见《Delphi图像处理 -- 数据类型及内部过程》。

尽管我十分努力,但水平有限,错误在所难免,欢迎指正和指导。邮箱地址:

[email protected]

注:本文章已于2009.10.27重新整理过。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
理解 Delphi 的类(十) - 深入方法[8] - 如果忘了返回值发布时间:2022-07-18
下一篇:
Delphi新语法For..In发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap