2

我正在使用 FastJpeg 库 (jpegdec.pas) 将 JPEG 帧解码为 Graphics.TBitmap 对象。解码工作正常,我使用 TBitmap.SaveToFile() 方法将位图打印到文件以进行视觉检查,它看起来很棒。然后我使用 TBitmap 句柄调用 GetObject() 以获取 TDibSection 对象。返回的 TDibSection 对象确实显示了顶级字段(bmWidth、bmHeight 等)的正确值,尽管 bmBit 为 NIL,我发现令人惊讶的是,因为 SaveToFile() 调用确实将图像正确地写入了磁盘。我遇到的问题是 TBitmapHeaderInfo 字段(dsBmih)全为零。此外,如果重要的话,dsBitFields、dshSection 和 dsOffset 字段也都为零。就好像它填写了主要字段,之后的所有内容都被忽略了。这里'

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

下面的代码基本上显示了我在做什么。为什么我得到一个空白的 TBitmapHeaderInfo 字段?这导致我对 AVI dll 的调用出现问题,因此我需要解决此问题。

这是代码片段

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;

更新:为了回应 TLama 的评论,我更新了代码,如下所示。现在可以了。我有一些疑问:

1)代码可以精简吗?它显然比上面的原始代码复杂得多,也许我执行了太多步骤。

2) 我是否释放了所有内存并释放了我需要的所有 GDI 句柄和对象?我不想要任何内存泄漏。

这是更新的代码:

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;
4

1 回答 1

3

由于您只需要从 JPEG 文件解码的第一帧的 DIB 部分的句柄,我认为使用CreateDIBSection并复制到由函数调用分配的内存块中的 指向的内容TJpegDecode.pRGB应该是足够的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;
于 2012-05-01T21:49:47.037 回答