Jump to content
Korean Random
StranikS_Scan

Редактирование DDS и сжатие в DXT1 на Delphi

Recommended Posts

Значится продолжаю работать над реанимацией MCTCreator. Сейчас остановился на покраске танков, статически расположенных на картах. Расковырял space.bin, покопался в механизме, Значится пришел к следующему - больше патчить space.bin не требуется и я этого делать не буду. Достаточно перекрасить отдельные DDS файлы и всё. Но вот дальше есть проблемы.

 

Файлы DDS в формате DXT1 с MipMap. Обнаружил, что они и после перекраски должны быть в сжатом формате DXT1 и обязательно иметь MipMap.  Иначе клиент их не отображает. Однако на Delphi проблематично найти или написать код на сжатие в DXT1. Я нашел библиотеку GLScene под Delphi, в ней есть класс TGLDDSImage который умеет читать сжатый DDS, в этом классе я могу получить прямой доступ к каждому блоку сжатого изображения, я могу создавать MipMap, я могу сконвертировать DXT1 в Bitmap32 и его перекрасить, но увы я не могу самого главного - я не могу сконвертировать Bitmap32 обратно в DXT1. В библиотеке GLScene такая возможность просто отсутствует, такого кода нет. Точно также как я нигде не нашел его под Delphi на профильных сайтах и форумах.

 

Потому нужна помощь, кто видел или знает как на Делфи сжать в DXT1 изображение отзовитесь. Либо как альтернативна, поделитесь компактной консольной программкой для конвертирования BMP или несжатых DDS в DXT1, я её тогда положу в комплект с MCTCreator-ом и он будет её вызывать для своих нужд.

 

Под спойлером немного кода из GLScene с комментариями.

 

procedure TForm1.Button1Click(Sender: TObject);
var s:TGLDDSImage; 
    i,n,size: Cardinal; 
    newData: PGLPixel32Array; //МАССИВ ПИКСЕЛЕЙ, ГДЕ ЭЛЕМЕНТАМИ ЯВЛЯЮТСЯ ЗАПИСИ ВИДА record r,g,b,a: Byte end 
begin
s:=TGLDDSImage.Create; //КЛАСС ДЛЯ РАБОТЫ ЧТЕНИЯ DDS из GLScene 
s.LoadFromFile('123.dds');
s.UnMipmap; //МОГУ УДАЛИТЬ МИПМАПЫ, МОГУ ИХ СОЗДАТЬ 
size := s.LevelWidth[0] * s.LevelHeight[0] * 4;
GetMem(newData, size);
ConvertImage( //КОНВЕРТИРУЮ МАССИВ СО СЖАТЫМ ИЗОБРАЖЕНИЕМ В МАССИВ С ПИКСЕЛЯМИ Bitmap32
  s.Data, newData, s.ColorFormat, GL_RGBA, s.DataType, GL_BYTE, s.LevelWidth[0], s.LevelHeight[0]);
n:=s.LevelWidth[0]*s.LevelHeight[0]; 
for i:=0 to n-1 do 
  begin newData[i].r:=$FF; //ПЕРЕКРАШИВАЮ ПИКСЕЛИ 
  newData[i].g:=$00; 
  newData[i].b:=$00; 
  newData[i].a:=$00;
  end; 
Caption:=IntToStr(i); 
ConvertImage( //------- ЭТОТ КОД НЕ РАБОТАЕТ, ТАК КАК НЕ ВОЗМОЖНО СКОНВЕРТИРОВАТЬ ОБРАТНО В DXT1, ТАКОЙ КОД ТУПО ОТСУСТВУЕТ В БИБЛИОТЕКЕ -------------
  newData, s.Data, GL_RGBA, s.ColorFormat, GL_BYTE, s.DataType, s.LevelWidth[0], s.LevelHeight[0]);
FreeMem(newData);
s.SaveToFile('345.dds'); 
s.Free; 
end;

 


//А ВОТ ЭТО КОД ИЗ БИБЛИОТЕКИ ДЛЯ КОНВЕРТИРОВАНИЯ МАССИА ПИКСЕЛЕЙ ИЗ DXT1 В Bitmap32

procedure DXT1_ToImf(ASource: Pointer; ADest: PIntermediateFormatArray; AColorFormat: TGLEnum; AWidth, AHeight: Integer);
  var
    x, y, i, j, k, select, offset: Integer;
    col0, col1: Word;
    colors: TU48BitBlock;
    bitmask: Cardinal;
    temp: PGLubyte;
    r0, g0, b0, r1, g1, b1: Byte;
  begin

    temp := PGLubyte(ASource);
    for y := 0 to (AHeight div 4) - 1 do
    begin
      for x := 0 to (AWidth div 4) - 1 do
      begin
        col0 := PWord(temp)^;
        Inc(temp, 2);
        col1 := PWord(temp)^;
        Inc(temp, 2);
        bitmask := PCardinal(temp)^;
        Inc(temp, 4);

        DecodeColor565(col0, r0, g0, b0);
        DecodeColor565(col1, r1, g1, b1);

        colors[0][0] := r0 shl 3;
        colors[0][1] := g0 shl 2;
        colors[0][2] := b0 shl 3;
        colors[0][3] := $FF;
        colors[1][0] := r1 shl 3;
        colors[1][1] := g1 shl 2;
        colors[1][2] := b1 shl 3;
        colors[1][3] := $FF;

        if col0 > col1 then
        begin
          colors[2][0] := (2 * colors[0][0] + colors[1][0] + 1) div 3;
          colors[2][1] := (2 * colors[0][1] + colors[1][1] + 1) div 3;
          colors[2][2] := (2 * colors[0][2] + colors[1][2] + 1) div 3;
          colors[2][3] := $FF;
          colors[3][0] := (colors[0][0] + 2 * colors[1][0] + 1) div 3;
          colors[3][1] := (colors[0][1] + 2 * colors[1][1] + 1) div 3;
          colors[3][2] := (colors[0][2] + 2 * colors[1][2] + 1) div 3;
          colors[3][3] := $FF;
        end
        else
        begin
          colors[2][0] := (colors[0][0] + colors[1][0]) div 2;
          colors[2][1] := (colors[0][1] + colors[1][1]) div 2;
          colors[2][2] := (colors[0][2] + colors[1][2]) div 2;
          colors[2][3] := $FF;
          colors[3][0] := (colors[0][0] + 2 * colors[1][0] + 1) div 3;
          colors[3][1] := (colors[0][1] + 2 * colors[1][1] + 1) div 3;
          colors[3][2] := (colors[0][2] + 2 * colors[1][2] + 1) div 3;
          colors[3][3] := 0;
        end;

        k := 0;
        for j := 0 to 3 do
        begin
          for i := 0 to 3 do
          begin
            select := (bitmask and (3 shl (k * 2))) shr (k * 2);
            if ((4 * x + i) < AWidth) and ((4 * y + j) < AHeight) then
            begin
              offset := ((4 * y + j) * AWidth + (4 * x + i));
              ADest[offset].B := colors[select][0];
              ADest[offset].G := colors[select][1];
              ADest[offset].R := colors[select][2];
              ADest[offset].A := colors[select][3];
            end;
            Inc(k);
          end;
        end;

      end;
    end;
  end;

 

ЗЫ: Меня немного удивляет почему в GLScene есть функция на декодирования DXT1 в Bitmap32 и нигде нет обратной. Хотя я не шибко разбираюсь в моделях и форматах к ним.

Edited by StranikS_Scan

Share this post


Link to post

Short link
Share on other sites

Я использовал Crystaldxt в CCAtlas.

18 минут назад, StranikS_Scan сказал:

Меня немного удивляет почему в GLScene есть функция на декодирования DXT1 в Bitmap32 и нигде нет обратной.

Наверное потому что, для декодирования существует только один алгоритм, и он описан в стандарте. А для кодирования каждый придумывает свой.

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
15 минут назад, ktulho сказал:

Я использовал Crystaldxt в CCAtlas.

Наверное потому что, для декодирования существует только один алгоритм, и он описан в стандарте. А для кодирования каждый придумывает свой.

 

Во! Попробую заюзать.

Ну да, это же сжатие с потерями.

Share this post


Link to post

Short link
Share on other sites

Во чё нагуглил:

DXT Libraries

  • stb_dxt.h is a single file, public domain DXT1/DXT5 compressor by Sean Barrett based on work by Fabian Giesen.
  • squish is one of the oldest libraries, it dates back to 2006 and was written by Simon Brown. He links to libsquish version 1.10 which hasn't been updated in three years, but apparently others have continued development and I've seen versions as high as 1.15.
  • Crunch takes a novel approach to DXT compression, it compresses the DXT data with the goal of making it more compressible, and then runs a compressor on that data and stores it in their own .CRN file format. There are a ton of games that use Crunch, and it is a good way to go if you are looking for maximum compression. In the end my goal was not maximum compression, and based on Crunch's documentation I thought that the Crunch utility itself did not come with full source. I found out later that the full source is on github.
  • Basis is the next generation of Crunch and is under active development. It is a paid product and you can learn more about it here.
  • Unity Crunch Fork is a fork of Crunch put out by the folks at Unity which makes Crunch a little faster, compress a little better and support some new bitmap formats. Learn more about it on the Unity blog.
  • bimg is an image library which includes the texturec tool for compressing bitmaps to DXT among other formats. I found out about this too late, but you can check it out on github (texturec.)
  • Compressonator is from AMD and is open source. I did a lot of research trying to figure out which DXT compressor produced the bast looking results. There aren't a lot of subjective comparisons, however the anecdotal consensus is that Compressonator produces the best looking DXT files. Compressonator comes in both a GUI version with an image comparison tool as well as a command line tool suitable for scripting, and it can generate MIP maps. The only glitch I ran into is that on my Windows 7 machine Compressonator tries to load a Windows 10 .DLL and pops up a error dialog. The people working on Compressonator were very quick to come up with a workaround.
  • NVidia Texture Tools are based on libsquish but also include a GPU accelerated version. More info here.

 

Походу Compressonator от AMD поддерживает и CLI и mipmaps, так что начать стоит с него)

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
1 час назад, StranikS_Scan сказал:

@ktulho а создания мипмапов в нём нет? Я чета не вижу.

Вроде есть. До дома доберусь, посмотрю. Помниться мне писали, что CCAtlas с мтнимапами создавал атласы.

Share this post


Link to post

Short link
Share on other sites

Действительно нет. Про мипмапы в теме TankIconMaker писали.

Share this post


Link to post

Short link
Share on other sites
1 час назад, ktulho сказал:

Действительно нет. Про мипмапы в теме TankIconMaker писали.

 

Придётся колхозить, попробую в CrystalDXT прикрутить код из GLScene благо мипмапы не шибко сложная вещь.

Share this post


Link to post

Short link
Share on other sites
21 час назад, ktulho сказал:

 

Ооо, яа, яа, зер гуд! :harp:

Тема закрыта.

 

Ниже выкладываю доработанную мной немного версию CrystalDXT, взятую с репо CrystalAtf, и пример процедуры для записи одноцветного DDS DXT1/DXT5 с альфой и мипмапом.

 

CrystalDXT.pas

//Создание DDS формата DXT1 или DXT5 с заполнением единым цветом RGBA c MipMap
procedure CreateDDS(const FileName: string; const Width, Height: Cardinal; const Color: string; const Dxt5Format: Boolean; const MipMap: Boolean);
var PixColor: TRGBQuad;
    Header: TDxtHeader;
    DxtFormat: AtfFormat;
    Buffer: TMemoryStream;
    DxtBlocks: Pointer;
    i, NumMips: Integer;
    Size: Cardinal;
    Sizes: TSizes;
    Bitmap: TBitmap;
    SLine: PRGBQuad;
    x, y, W, H: Cardinal;
begin
PixColor:=GetColor(Color);
if Dxt5Format then
 DxtFormat:=DXT5
else DxtFormat:=DXT1;
W:=Width;
H:=Height;
Buffer:=TMemoryStream.Create;
try
//Покраска
Bitmap:=TBitmap.Create;
try
with Bitmap do
 begin
 PixelFormat:=pf32bit;
 Width:=W;
 Height:=H;
 for y:=0 to H-1 do
  begin
  SLine:=ScanLine[y];
  for x:=0 to W-1 do
   begin
   SLine^:=PixColor;
   Inc(SLine);
   end;
  end;
 end;
//Мипмапы
NumMips:=0;
if MipMap then
 begin
 Size:=0;
 while (W>1)or(H>1) do
  begin
  Sizes[NumMips]:=DxtImageSize(DxtFormat, W, H);
  Inc(Size, Sizes[NumMips]);
  if W>1 then
    W:=W div 2;
  if H>1 then
    H:=H div 2;
  Inc(NumMips);
  end;
 Sizes[NumMips]:=DxtImageSize(DxtFormat, 1, 1);
 Inc(Size, Sizes[NumMips]);
 end
else begin
     Size:=DxtImageSize(DxtFormat, W, H);
     Sizes[0]:=Size;
     end;
//Запись
Header:=DxtHeader(Width, Height, NumMips+1, DxtFormat);
Buffer.Write(Header, SizeOf(Header));
Buffer.Size:=SizeOf(Header)+Size;
Size:=Integer(Buffer.Memory)+SizeOf(Header);
DxtBlocks:=Pointer(Size);
i:=0;
repeat
 DxtImageCompressParallel(DxtBlocks, not Dxt5Format, Bitmap.Width, Bitmap.Height,
                          Pointer(Bitmap), GetBitmap32bitBlock);
 if NumMips>0 then
  begin
  Inc(Size, Sizes[i]);
  DxtBlocks:=Pointer(Size);
  MipMapCompress(Bitmap);
  end;
 Inc(i); 
 until i>NumMips;
Buffer.SaveToFile(FileName);
finally
Bitmap.Free;
end;
finally
Buffer.Free;
end;
end;

 

 

Edited by StranikS_Scan

Share this post


Link to post

Short link
Share on other sites
Guest
This topic is now closed to further replies.

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...