2 votes

Graphics.TBitmap décodé avec succès à partir d'une image JPEG produit une TDibSection vide après l'appel à GetObject()

Je décode des images JPEG en objets Graphics.TBitmap en utilisant la bibliothèque FastJpeg (jpegdec.pas). Le décodage fonctionne bien et j'imprime le bitmap dans un fichier pour une inspection visuelle en utilisant la méthode TBitmap.SaveToFile(). J'appelle ensuite GetObject() en utilisant le handle TBitmap pour obtenir un objet TDibSection. L'objet TDibSection retourné montre les valeurs correctes pour les champs de premier niveau (bmWidth, bmHeight, etc.) bien que bmBit soit NIL et je trouve cela surprenant étant donné que l'appel SaveToFile() a écrit l'image sur le disque correctement. Le problème que je rencontre est que le champ TBitmapHeaderInfo (dsBmih) ne contient que des zéros. Les champs dsBitFields, dshSection et dsOffset sont également tous à zéro, si cela a de l'importance. C'est comme s'il avait rempli les champs primaires et que tout le reste avait été laissé de côté. Voici un extrait de l'objet TDibSection renvoyé :

dsBm: (0, 320, 240, 1280, 1, 32, nil)
dsBmih: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
dsBitfields: (0, 0, 0)
dshSection: 0
dsOffset: 0

Le code ci-dessous montre en gros ce que je fais. Pourquoi est-ce que je reçois un champ TBitmapHeaderInfo vide ? Cela cause des problèmes avec mes appels à la dll AVI et je dois donc résoudre ce problème.

Voici l'extrait de code

var
    theBitmap: Graphics.TBitmap;
    aryBytes: TDynamicByteArray;
    dibs: TDibSection;
begin
    aryBytes := nil;

    // The following function loads JPEG frame #0 from a collection of JPEG frames.
    aryBytes := LoadJpegFrame(0);

    // Decode the first JPEG frame so we can pass it to the compressor
    //  selector call.
    theBitmap := JpegDecode(@aryBytes[0], Length(aryBytes));

    if GetObject(theBitmap.Handle, sizeof(dibs), @dibs) = 0 then
        raise Exception.Create('Get Object failed getting the TDibSection information for the bitmap.');

    // ... The TBitmapHeaderInfo field in dibs is empty as described in the post.
end;

MISE À JOUR : En réponse au commentaire de TLama, j'ai mis à jour le code comme vous pouvez le voir ci-dessous. Il fonctionne maintenant. J'ai quelques questions à poser :

1) Le code peut-il être simplifié ? Il est manifestement beaucoup plus compliqué que le code original ci-dessus et j'effectue peut-être trop d'étapes.

2) Est-ce que je libère toute la mémoire et tous les objets et handles GDI dont j'ai besoin ? Je ne veux pas de fuites de mémoire.

Voici le code mis à jour :

var
    hr: HRESULT;
    bmi: TBitmapInfo;
    pImg: PJpegDecode;
    jpegDecodeErr: TJpegDecodeError;
    hbm: HBITMAP;
    pBits: Pointer;
begin
    hr := 0; pImg := nil; hbm := 0; pBits := nil;

    try
        jpegDecodeErr := JpegDecode(@aryBytes[0], Length(aryBytes), pImg);

        if jpegDecodeErr <> JPEG_SUCCESS then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) The bitmap failed to decode with error code: ' + IntToStr(Ord(jpegDecodeErr)));

        if not Assigned(pImg) then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) The bitmap decoded from the first JPEG frame in the video file is unassigned: ' + fullVideoInFilename);

        pImg^.ToBMI(bmi);

        theBitmap := pImg.ToBitmap;

        // Now create a DIB section.
        hbm := CreateDIBSection(theBitmap.Handle, bmi, DIB_RGB_COLORS, pBits, 0, 0);

        if hbm = ERROR_INVALID_PARAMETER then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) One of the parameters passed to CreateDIBSection is invalid.');

        if hbm = 0 then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) The call to CreateDIBSection failed.');

        // Select the compressor.  This call USED to fail before TLama's
        //  suggestion.  Now it works.
        hr := aviMaker.compression(hbm, nil, true, Self.Handle);

        if hr <> S_OK then
            raise Exception.Create('(TfrmMain_moviemaker_.cmdTestClick) Error during compressor selector call: ' + FormatAviMessage(hr));
    finally
        if Assigned(pImg) then
        begin
            pImg^.Free;
            pImg := nil;
        end;

        if Assigned(theBitmap) then
            FreeAndNil(theBitmap);

        if hbm > 0 then
            DeleteObject(hbm);
    end; // try (2)
end;

3voto

TLama Points 40454

Puisque vous n'avez besoin que d'un handle vers la section DIB de la première image décodée à partir du fichier JPEG, je pense qu'il devrait suffire d'utiliser la fonction CreateDIBSection et copier dans le bloc de mémoire alloué par l'appel de fonction le contenu pointé par la balise TJpegDecode.pRGB qui devraient déjà correspondre aux valeurs de la section DIB.

procedure ProbeGetObject(ACanvas: TCanvas; AGDIObject: HGDIOBJ);
var
  Width, Height: Integer;
  DIBSection: TDIBSection;
  BitmapInfo: TBitmapInfo;
begin
  if GetObject(AGDIObject, SizeOf(DIBSection), @DIBSection) <> 0 then
  begin
    FillChar(BitmapInfo, SizeOf(BitmapInfo), 0);
    BitmapInfo.bmiHeader := DIBSection.dsBmih;
    // if you comment the following line, the image will be rendered flipped
    BitmapInfo.bmiHeader.biHeight := - BitmapInfo.bmiHeader.biHeight;
    Width := DIBSection.dsBm.bmWidth;
    Height := Abs(DIBSection.dsBm.bmHeight);
    StretchDIBits(ACanvas.Handle, 0, 0, Width, Height, 0, 0, Width, Height,
      DIBSection.dsBm.bmBits, BitmapInfo, DIB_RGB_COLORS, SRCCOPY);
  end;
end;

procedure InitializeCompressor(Buffer: Pointer; BufferLen: Integer);
var
  ScreenDC: HDC;
  DIBHandle: HBITMAP;
  DIBValues: Pointer;
  BufferSize: DWORD;
  BufferHandle: THandle;
  BufferPointer: Pointer;
  JPEGImage: PJpegDecode;
  BitmapInfo: TBitmapInfo;
begin
  if JpegDecode(Buffer, BufferLen, JPEGImage) = JPEG_SUCCESS then
  try
    JPEGImage^.ToBMI(BitmapInfo);
    BufferSize := Abs(BitmapInfo.bmiHeader.biWidth *
      BitmapInfo.bmiHeader.biHeight * 4);
    BufferHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil,
      PAGE_READWRITE, 0, BufferSize, nil);
    if BufferHandle <> 0 then
    try
      BufferPointer := MapViewOfFile(BufferHandle, FILE_MAP_WRITE, 0, 0, 0);
      if Assigned(BufferPointer) then
      begin
        CopyMemory(BufferPointer, JPEGImage^.pRGB, BufferSize);
        ScreenDC := GetDC(0);
        if ScreenDC <> 0 then
        try
          DIBHandle := CreateDIBSection(ScreenDC, BitmapInfo, DIB_RGB_COLORS,
            DIBValues, BufferHandle, 0);
          if (DIBHandle <> 0) and Assigned(DIBValues) then
          try
            ProbeGetObject(Form1.Canvas, DIBHandle);
            // here try to initialize the compressor, the DIB section should
            // contain values obtained from the JPEG decoder; in the DIBHandle
            // variable is the handle to the DIB section
          finally
            DeleteObject(DIBHandle);
          end;
        finally
          ReleaseDC(0, ScreenDC);
        end;
      end;
    finally
      CloseHandle(BufferHandle);
    end;
  finally
    JPEGImage^.Free;
  end;
end;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X