{%MainUnit ../graphics.pp}

{******************************************************************************
                                     TRasterImage
 ******************************************************************************

 *****************************************************************************
  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.
 *****************************************************************************
}

procedure TRasterImage.Assign(Source: TPersistent);
  procedure CopyMask(AMask: HBITMAP);
  var
    RI: TRawImage;
    msk, dummy: HBITMAP;
    Res: Boolean;
  begin
    // we need a winapi.CopyImage here (would make things easier)
    // in theory, it should not matter if a HBITMAP was created as bitmap or as mask
    // since there is a description problem in gtk, create both (we cannot create mask only)
    // todo: fix gtk
    if not RawImage_FromBitmap(RI, AMask, AMask) then Exit;
    msk := 0;
    dummy := 0;
    RawImage_CreateBitmaps(RI, dummy, msk {, True});
    RI.FreeData;
    DeleteObject(dummy);

    if BitmapHandleAllocated
    then Res := UpdateHandles(BitmapHandle, msk)
    else Res := UpdateHandles(0, msk);
    if not Res then DeleteObject(msk);
  end;


var
  SrcImage: TRasterImage absolute Source;
  SrcFPImage: TFPCustomImage absolute Source;
  SrcRawImage, DstRawImage: PRawImage;
  IntfImage: TLazIntfImage;
  ImgHandle,ImgMaskHandle: HBitmap;
begin
  if Source = Self then exit;

  if Source is TRasterImage
  then begin
    if MaskHandleAllocated
    then begin
      // Clear mask first mask
      if BitmapHandleAllocated
      then UpdateHandles(BitmapHandle, 0)
      else UpdateHandles(0, 0);
    end;

    FTransparentMode := SrcImage.FTransparentMode;
    FTransparentColor := SrcImage.FTransparentColor;
    FMasked := SrcImage.FMasked;

    // -> check if already shared
    if SrcImage.FSharedImage <> FSharedImage
    then begin
      // image is not shared => new image data
      // -> free canvas (interface handles)
      FreeCanvasContext;
      // release old FImage
      FSharedImage.Release;

      // We only can share images of the same type ...
      if CanShareImage(SrcImage.GetSharedImageClass)
      then begin
        // share FImage with assigned graphic
        FSharedImage := SrcImage.FSharedImage;
        FSharedImage.Reference;

        // when updating, unshare
        // Since we "share" it first, the unshare code will create a copy
        if (FUpdateCount > 0)
        or (SrcImage.FUpdateCount > 0)
        then begin
          UnshareImage(True);
          FreeSaveStream;
        end;
      end
      else begin
        // not sharable, create rawimage copy
        FSharedImage := GetSharedImageClass.Create;
        FSharedImage.Reference;

        // copy raw image
        SrcRawImage := SrcImage.GetRawImagePtr;
        DstRawImage := GetRawImagePtr;
        if (SrcRawImage <> nil) and (DstRawImage <> nil)
        then with SrcRawImage^ do
          ExtractRect(Rect(0, 0, Description.Width, Description.Height), DstRawImage^);
      end;
    end;

    if SrcImage.MaskHandleAllocated
    then CopyMask(SrcImage.MaskHandle);


    if FUpdateCount = 0
    then Changed(Self);

    Exit;
  end;

  if Source is TFPCustomImage
  then begin
    // todo: base on rawimage
  
    IntfImage := TLazIntfImage.Create(0,0,[]);
    try
      if BitmapHandleAllocated
      then IntfImage.DataDescription := GetDescriptionFromBitmap(BitmapHandle, 0, 0)
      else IntfImage.DataDescription := GetDescriptionFromDevice(0, 0, 0);
      IntfImage.Assign(SrcFPImage);
      IntfImage.CreateBitmaps(ImgHandle, ImgMaskHandle);
      SetHandles(ImgHandle, ImgMaskHandle);
    finally
      IntfImage.Free;
    end;
    if FUpdateCount = 0
    then Changed(Self);
    
    Exit;
  end;

  // fall back to default
  inherited Assign(Source);
end;

procedure TRasterImage.BeginUpdate(ACanvasOnly: Boolean);
begin
  if FUpdateCount = 0
  then begin
    UnshareImage(True);
    FUpdateCanvasOnly := ACanvasOnly;
  end
  else begin
    // if we are updating all, then requesting a canvas only won't change it
    // if we are updating canvas only, then requesting all is an error
    if FUpdateCanvasOnly and not ACanvasOnly
    then raise EInvalidGraphicOperation.Create(rsRasterImageUpdateAll);
  end;

  Inc(FUpdateCount);
end;

procedure TRasterImage.Clear;
begin
  if Empty then Exit;

  FreeSaveStream;
  SetSize(0, 0);
  if FUpdateCount = 0
  then Changed(Self);
end;

procedure TRasterImage.BitmapHandleNeeded;
var
  ImgHandle, ImgMaskHandle: HBitmap;
  ImagePtr: PRawImage;
  DevDesc: TRawImageDescription;
  QueryFlags: TRawImageQueryFlags;
  W, H: Integer;
  SkipMask: Boolean;
begin
  if BitmapHandleAllocated then exit;

  ImagePtr := GetRawImagePtr;
  if ImagePtr = nil then Exit;

  ImgMaskHandle := 0;

  // we must skip mask creation if
  // a) we already have mask
  // b) mask needs to be created another way - using TransparentColor
  // c) there's no mask data
  SkipMask := MaskHandleAllocated
           or (TransparentMode = tmFixed)
           or not ImagePtr^.IsMasked(False);

  if not CreateCompatibleBitmaps(ImagePtr^, ImgHandle, ImgMaskHandle, SkipMask)
  then begin
    {$IFNDEF DisableChecks}
    DebugLn('TRasterImage.BitmapHandleNeeded: Unable to create handles, using default');
    {$ENDIF}
    // create a default handle
    W := Width;
    if W < 1 then W := 1;
    H := Height;
    if H < 1 then H := 1;

    if ImagePtr^.Description.Depth = 1
    then QueryFlags := [riqfMono]
    else QueryFlags := [riqfRGB];
    if ImagePtr^.Description.AlphaPrec <> 0
    then Include(QueryFlags, riqfAlpha);
    if ImagePtr^.Description.MaskBitsPerPixel <> 0
    then Include(QueryFlags, riqfMask);
    QueryDescription(DevDesc, QueryFlags, W, H);
    ImgHandle := CreateDefaultBitmapHandle(DevDesc);
  end;

  // if we do not have new mask then either use old one or use none
  if SkipMask and MaskHandleAllocated
  then UpdateHandles(ImgHandle, MaskHandle)
  else UpdateHandles(ImgHandle, ImgMaskHandle);
end;

function TRasterImage.CanShareImage(AClass: TSharedRasterImageClass): Boolean;
begin
  // We only can share images of the same type.
  // And if we are some "end" graphic type
  Result := (AClass <> TSharedCustomBitmap)
        and (AClass = GetSharedImageClass);
end;


procedure TRasterImage.Draw(DestCanvas: TCanvas; const DestRect: TRect);
var
  UseMaskHandle: HBitmap;
  SrcDC: hDC;
  DestDC: hDC;
begin
  if (Width=0) or (Height=0)
  then Exit;

  BitmapHandleNeeded;
  if not BitmapHandleAllocated then Exit;

  if Masked then
    UseMaskHandle:=MaskHandle
  else
    UseMaskHandle:=0;

  SrcDC := Canvas.GetUpdatedHandle([csHandleValid]);
  DestCanvas.Changing;
  DestDC := DestCanvas.GetUpdatedHandle([csHandleValid]);
  StretchMaskBlt(DestDC,
          DestRect.Left,DestRect.Top,
          DestRect.Right-DestRect.Left,DestRect.Bottom-DestRect.Top,
          SrcDC,0,0,Width,Height, UseMaskHandle,0,0,DestCanvas.CopyMode);
  DestCanvas.Changed;
end;

procedure TRasterImage.EndUpdate(AStreamIsValid: Boolean = False);
begin
  if FUpdatecount = 0
  then raise EInvalidGraphicOperation.Create(rsRasterImageEndUpdate);
  
  Dec(FUpdatecount);
  if FUpdatecount > 0 then Exit;

  if not FUpdateCanvasOnly
  then begin
    FreeCanvasContext;
    // delete bitmaphandle too
    if BitmapHandleAllocated
    then DeleteObject(InternalReleaseBitmapHandle)

  end;

  if not AStreamIsValid
  then FreeSaveStream;
  Changed(Self);
end;

constructor TRasterImage.Create;
begin
  inherited Create;
  FSharedImage := GetSharedImageClass.Create;
  FSharedImage.Reference;
  FTransparentColor := clDefault; // for Delphi compatibility. clDefault means:
                                  // use Left,Bottom pixel as transparent pixel
end;

destructor TRasterImage.Destroy;
begin
  FreeCanvasContext;
  FSharedImage.Release;
  FSharedImage := nil;
  FreeAndNil(FCanvas);
  inherited Destroy;
end;

procedure TRasterImage.FreeCanvasContext;
begin
  if (FCanvas <> nil) then TBitmapCanvas(FCanvas).FreeDC;
end;

function TRasterImage.GetCanvas: TCanvas;
begin
  if FCanvas = nil then
    CreateCanvas;
  Result := FCanvas;
end;

procedure TRasterImage.CreateCanvas;
begin
  if FCanvas <> nil then Exit;

  FCanvas := TBitmapCanvas.Create(Self);
  FCanvas.OnChanging := @CanvasChanging;
  FCanvas.OnChange := @Changed;
end;

procedure TRasterImage.FreeImage;
begin
  SetHandle(0);
end;

procedure TRasterImage.LoadFromBitmapHandles(ABitmap, AMask: HBitmap; ARect: PRect);
var
  RawImg: TRawImage;
  ImgHandle, ImgMaskHandle: HBitmap;
begin
  //DebugLn('TRasterImage.CreateFromBitmapHandles A SrcRect=',dbgs(SrcRect));
  if not RawImage_FromBitmap(RawImg, ABitmap, AMask, ARect) then
    raise EInvalidGraphicOperation.Create('TRasterImage.LoadFromBitmapHandles Get RawImage');
  ImgHandle:=0;
  ImgMaskHandle:=0;
  try
    //DebugLn('TRasterImage.CreateFromBitmapHandles B SrRect=',dbgs(SrcRect));
    if not RawImage_CreateBitmaps(RawImg, ImgHandle, ImgMaskHandle) then
      raise EInvalidGraphicOperation.Create('TRasterImage.LoadFromBitmapHandles Create bitmaps');
    SetHandles(ImgHandle, ImgMaskHandle);
    ImgHandle:=0;
    ImgMaskHandle:=0;
  finally
    RawImg.FreeData;
    if ImgHandle<>0 then DeleteObject(ImgHandle);
    if ImgMaskHandle<>0 then DeleteObject(ImgMaskHandle);
  end;
end;

procedure TRasterImage.LoadFromDevice(DC: HDC);
var
  IntfImg: TLazIntfImage;
  ImgHandle, ImgMaskHandle: HBitmap;
begin
  ImgHandle:=0;
  ImgMaskHandle:=0;
  IntfImg:=nil;
  try
    // create the interface image
    IntfImg:=TLazIntfImage.Create(0,0,[]);
    // get a snapshot
    IntfImg.LoadFromDevice(DC);
    // create HBitmap
    IntfImg.CreateBitmaps(ImgHandle, ImgMaskHandle);
    // feed HBitmap into a TRasterImage
    SetHandles(ImgHandle, ImgMaskHandle);
    ImgHandle:=0;
    ImgMaskHandle:=0;
  finally
    IntfImg.Free;
    if ImgHandle<>0 then DeleteObject(ImgHandle);
    if ImgMaskHandle<>0 then DeleteObject(ImgMaskHandle);
  end;
end;

procedure TRasterImage.Mask(ATransparentColor: TColor);
begin
  CreateMask(ATransparentColor);
end;

procedure TRasterImage.SetTransparentColor(AValue: TColor);
begin
  if FTransparentColor = AValue then exit;
  FTransparentColor := AValue;

  if AValue = clDefault
  then FTransparentMode := tmAuto
  else FTransparentMode := tmFixed;

  if MaskHandleAllocated
  then MaskHandle := 0
  else Changed(Self);
end;

procedure TRasterImage.Changed(Sender: TObject);
begin
  if FUpdateCount > 0 then Exit;

  //FMaskBitsValid := False;
  if Sender = FCanvas
  then FreeSaveStream;

  inherited Changed(Sender);
end;

function TRasterImage.CreateDefaultBitmapHandle(
  const ADesc: TRawImageDescription): HBITMAP;
begin
  Result := 0;
end;

procedure TRasterImage.CanvasChanging(Sender: TObject);
begin
  if FUpdateCount > 0 then Exit;

  // called before the canvas is modified
  // -> make sure the handle is unshared (otherwise the modifications will also
  //    modify all copies)
  // -> Savestream will be freed when changed (so it can be loaded by canvas)
  UnshareImage(True);
end;

procedure TRasterImage.LoadFromStream(AStream: TStream);
begin
  LoadFromStream(AStream, AStream.Size - AStream.Position);
end;

procedure TRasterImage.LoadFromStream(AStream: TStream; ASize: Cardinal);
var
  WorkStream: TMemoryStream;
  OldPos, NewSize: Int64;
begin
  BeginUpdate;
  UnshareImage(False);
  Clear; // clear old saved stream, allocated handles, etc
  if ASize = 0
  then begin
    EndUpdate(False);
    Exit;
  end;

  WorkStream := nil;
  try
    WorkStream := TMemoryStream.Create;
    WorkStream.SetSize(ASize);
    OldPos := AStream.Position;
    WorkStream.CopyFrom(AStream, ASize);
    WorkStream.Position := 0;
    ReadStream(WorkStream, ASize);
    NewSize := WorkStream.Position;
    if NewSize < ASize
    then begin
      // the size given was different than the image loaded
      // MWE: original code adjusted the streampos, do we stil need this ?
      AStream.Position := OldPos + NewSize;
      WorkStream.SetSize(NewSize);
    end;
    // Store our worstream as savestream
    FSharedImage.SaveStream := WorkStream;
    WorkStream := nil;
  finally
    // if something went wrong, free the workstream
    WorkStream.Free;
  end;
  EndUpdate(True);
end;

function TRasterImage.GetRawImage: TRawImage;
var
  p: PRawImage;
begin
  p := GetRawImagePtr;
  if p = nil
  then Result{%H-}.Init
  else Result := p^;
end;

function TRasterImage.GetScanline(ARow: Integer): Pointer;
var
  RI: TRawImage;
begin
  RI := GetRawImage;
  Result := RI.GetLineStart(ARow);
end;

function TRasterImage.GetTransparentColor: TColor;
begin
  if FTransparentColor = clDefault
  then Result := RequestTransparentColor
  else Result := FTransparentColor;
end;

procedure TRasterImage.GetSupportedSourceMimeTypes(List: TStrings);
begin
  if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TCustomIcon) then
  begin
    List.Clear;
    List.Add(PredefinedClipboardMimeTypes[pcfBitmap]);
    List.Add(PredefinedClipboardMimeTypes[pcfPixmap]);
  end else
    inherited GetSupportedSourceMimeTypes(List);
end;

function TRasterImage.GetTransparent: Boolean;
var
  Desc: PRawImageDescription;
begin
  if Masked
  then begin
    // postpone description generation since we know we are transparent here
    Result := True
  end
  else begin
    Desc := GetRawImageDescriptionPtr;
    Result := (Desc <> nil)
          and (Desc^.Format <> ricfNone)
          and (Desc^.AlphaPrec > 0);
    //TODO:  check for transparency through palette etc.
  end;
end;

function TRasterImage.GetWidth: Integer;
var
  Desc: PRawImageDescription;
begin
  Desc := GetRawImageDescriptionPtr;
  if (Desc = nil) or (Desc^.Format = ricfNone)
  then Result := 0
  else Result := Desc^.Width;
end;

function TRasterImage.HandleAllocated: boolean;
begin
  Result := FSharedImage.FHandle <> 0;
end;

function TRasterImage.GetMimeType: string;
begin
  {$IFDEF VerboseLCLTodos}{$note: implement}{$ENDIF}

  (*
  if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TCustomIcon) then begin
    if FSharedImage.SaveStream<>nil then begin
      case FSharedImage.SaveStreamType of
      bnXPixmap: Result:=PredefinedClipboardMimeTypes[pcfPixmap];
      else
        Result:=PredefinedClipboardMimeTypes[pcfBitmap];
      end;
    end else
      Result:=PredefinedClipboardMimeTypes[pcfBitmap];
  end else
  *)

  Result:=inherited GetMimeType;
end;

procedure TRasterImage.LoadFromIntfImage(IntfImage: TLazIntfImage);
var
  ImgHandle, ImgMaskHandle: HBitmap;
begin
  IntfImage.CreateBitmaps(ImgHandle, ImgMaskHandle, not IntfImage.HasMask);
  SetHandles(ImgHandle, ImgMaskHandle);
end;

procedure TRasterImage.FreeSaveStream;
begin
  if FSharedImage.FSaveStream = nil then exit;
  //DebugLn(['TRasterImage.FreeSaveStream A ',ClassName,' ',FImage.FSaveStream.Size]);
  UnshareImage(false);
  FreeAndNil(FSharedImage.FSaveStream);
end;

procedure TRasterImage.LoadFromMimeStream(AStream: TStream; const AMimeType: string);
begin
  {$IFDEF VerboseLCLTodos}{$note Make oo}{$ENDIF}
  if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TCustomIcon) then begin
    if (AnsiCompareText(AMimeType,PredefinedClipboardMimeTypes[pcfBitmap])=0)
    or (AnsiCompareText(AMimeType,PredefinedClipboardMimeTypes[pcfPixmap])=0) then
    begin
      LoadFromStream(AStream);
      exit;
    end;
  end;
  inherited LoadFromMimeStream(AStream, AMimeType);
end;

procedure TRasterImage.LoadFromRawImage(const AImage: TRawImage; ADataOwner: Boolean);
var
  img: PRawImage;
begin
  BeginUpdate;
  try
    Clear;
    if AImage.Description.Format = ricfNone then Exit; // empty image

    img := GetRawImagePtr;

    // Make sure, we do not free AImage.Data or Mask
    if img^.Data = AImage.Data then begin
      img^.Data := nil;
      img^.DataSize := 0;
    end;
    if img^.Mask = AImage.Mask then begin
      img^.Mask := nil;
      img^.MaskSize := 0;
    end;
    if img^.Palette = AImage.Palette then begin
      img^.Palette := nil;
      img^.PaletteSize := 0;
    end;
    img^.FreeData;

    img^.Description := AImage.Description;
    if ADataOwner
    then begin
      img^.DataSize := AImage.DataSize;
      img^.Data := AImage.Data;
      img^.MaskSize := AImage.MaskSize;
      img^.Mask := AImage.Mask;
      img^.PaletteSize := AImage.PaletteSize;
      img^.Palette := AImage.Palette;
    end
    else begin
      // copy needed
      img^.DataSize := AImage.DataSize;
      if img^.DataSize > 0
      then begin
        GetMem(img^.Data, img^.DataSize);
        Move(AImage.Data^, img^.Data^, img^.DataSize);
      end
      else img^.Data := nil;

      img^.MaskSize := AImage.MaskSize;
      if img^.MaskSize > 0
      then begin
        GetMem(img^.Mask, img^.MaskSize);
        Move(AImage.Mask^, img^.Mask^, img^.MaskSize);
      end
      else img^.Mask := nil;

      img^.PaletteSize := AImage.PaletteSize;
      if img^.PaletteSize > 0
      then begin
        GetMem(img^.Palette, img^.PaletteSize);
        Move(AImage.Palette^, img^.Palette^, img^.PaletteSize);
      end
      else img^.Palette := nil;
    end;
  finally
    EndUpdate;
  end;
end;

procedure TRasterImage.SaveToStream(AStream: TStream);
  procedure Error;
  begin
    raise FPImageException.Create(rsErrorWhileSavingBitmap);
  end;
var
  Size, BytesWritten: Int64;
begin
  SaveStreamNeeded;
  if FSharedImage.SaveStream = nil
  then Error;

  FSharedImage.SaveStream.Position := 0;
  Size := FSharedImage.SaveStream.Size;
  if AStream is TMemoryStream
  then TMemoryStream(AStream).SetSize(AStream.Position + Size);

  BytesWritten := AStream.CopyFrom(FSharedImage.SaveStream, Size);
  if BytesWritten <> Size
  then Error;
end;

procedure TRasterImage.SetBitmapHandle(AValue: HBITMAP);
begin
  if MaskHandleAllocated
  then SetHandles(AValue, MaskHandle)
  else SetHandles(AValue, 0);
end;

procedure TRasterImage.SetHandle(AValue: THandle);
begin
  if FSharedImage.FHandle = AValue then Exit;
  FreeCanvasContext;
  UnshareImage(False);
  FSharedImage.FHandle := AValue;
  if FUpdateCount = 0
  then Changed(Self);
end;

procedure TRasterImage.SetMaskHandle(AValue: HBITMAP);
begin
  if BitmapHandleAllocated
  then SetHandles(BitmapHandle, AValue)
  else SetHandles(0, AValue);
end;

procedure TRasterImage.SetMasked(AValue: Boolean);
begin
  if AValue = Masked then Exit;

  FMasked := AValue;
  Changed(Self);
end;

procedure TRasterImage.SetTransparentMode(AValue: TTransparentMode);
begin
  if AValue = TransparentMode then exit;
  FTransparentMode := AValue;

  if AValue = tmAuto
  then TransparentColor := clDefault
  else TransparentColor := RequestTransparentColor;
end;

procedure TRasterImage.SetTransparent(AValue: Boolean);
var
  lTransparent: Boolean;
begin
  lTransparent := GetTransparent();
  if AValue = lTransparent then Exit;

  // some delphi compatibility, we can only change transparency through the mask.
  Masked := AValue;
end;

// release handles without freeing them
// useful for creating a HBitmap
function TRasterImage.ReleaseBitmapHandle: HBITMAP;
begin
  BitmapHandleNeeded;
  FreeCanvasContext;
  Result := InternalReleaseBitmapHandle;
end;

function TRasterImage.ReleaseMaskHandle: HBITMAP;
begin
  MaskHandleNeeded;
  FreeCanvasContext;
  Result := InternalReleaseMaskHandle;
end;

function TRasterImage.ReleasePalette: HPALETTE;
begin
  PaletteNeeded;
  FreeCanvasContext;
  Result := InternalReleasePalette;
end;

procedure TRasterImage.SaveStreamNeeded;
var
  WorkStream: TMemoryStream;
begin
  if FUpdateCount > 0
  then raise EInvalidGraphicOperation.Create(rsRasterImageSaveInUpdate);

  if FSharedImage.SaveStream <> nil then Exit;
  
  WorkStream := TMemoryStream.Create;
  try
    WriteStream(WorkStream);
    // Store our workstream as savestream
    FSharedImage.SaveStream := WorkStream;
    WorkStream := nil;
  finally
    // if something went wrong, free the workstream
    WorkStream.Free;
  end;
end;

function TRasterImage.CreateIntfImage: TLazIntfImage;
begin
  {$IFDEF VerboseLCLTodos}{$note todo: create based on rawimage}{$ENDIF}
  Result := TLazIntfImage.Create(0,0,[]);
  Result.LoadFromBitmap(BitmapHandle, MaskHandle);
end;

procedure TRasterImage.CreateMask(AColor: TColor);
var
  IntfImage: TLazIntfImage;
  ImgHandle, MskHandle: HBitmap;
  TransColor: TFPColor;
begin
  //DebugLn(['TRasterImage.CreateMask ',Width,'x',Height,' ',Transparent,' ',dbgs(ord(TransparentMode)),' ',dbgs(TransparentColor)]);
  if (Width = 0)
  or (Height = 0)
  or (AColor = clNone)
  or (  (FTransparentMode = tmFixed)
    and (FTransparentColor = clNone)
    and (AColor = clDefault)
  )
  then begin
    MaskHandle := 0;
    Exit;
  end;
  
  {$IFDEF VerboseLCLTodos}{$note todo: move to IntfImage}{$ENDIF}
  IntfImage := TLazIntfImage.Create(0,0,[]);
  try
    // force handle creation here, since at next step we will check for mask handle
    ImgHandle := BitmapHandle;
    // load from bitmap needs a mask handle otherwise no mask description is
    // created.
    if MaskHandleAllocated
    then MskHandle := MaskHandle 
    else MskHandle := CreateBitmap(Width, Height, 1, 1, nil);
    IntfImage.LoadFromBitmap(ImgHandle, MskHandle);
    if not MaskHandleAllocated
    then DeleteObject(MskHandle);
    ImgHandle := 0;

    if AColor = clDefault
    then begin
      if (FTransparentMode = tmFixed) and (FTransparentColor <> clDefault)
      then TransColor := TColorToFPColor(ColorToRGB(FTransparentColor))
      else TransColor := IntfImage.Colors[0, IntfImage.Height - 1];
    end
    else TransColor := TColorToFPColor(ColorToRGB(AColor));

    IntfImage.Mask(TransColor);

    IntfImage.CreateBitmaps(ImgHandle, MskHandle);
    MaskHandle := MskHandle;
    DeleteObject(ImgHandle);
  finally
    IntfImage.Free;
  end;
end;

function TRasterImage.GetEmpty: boolean;
begin
  Result := FSharedImage.IsEmpty;
end;

function TRasterImage.GetHandle: THandle;
begin
  HandleNeeded;
  Result := FSharedImage.FHandle;
end;

function TRasterImage.GetHeight: Integer;
var
  Desc: PRawImageDescription;
begin
  Desc := GetRawImageDescriptionPtr;
  if (Desc = nil) or (Desc^.Format = ricfNone)
  then Result := 0
  else Result := Desc^.Height;
end;

function TRasterImage.GetMasked: Boolean;
begin
  Result := FMasked;
end;

class function TRasterImage.GetSharedImageClass: TSharedRasterImageClass;
begin
  Result := TSharedRasterImage;
end;

procedure TRasterImage.GetSize(out AWidth, AHeight: Integer);
var
  Desc: PRawImageDescription;
begin
  Desc := GetRawImageDescriptionPtr;
  if (Desc = nil) or (Desc^.Format = ricfNone)
  then begin
    AWidth := 0;
    AHeight := 0;
  end
  else begin
    AWidth := Desc^.Width;
    AHeight := Desc^.Height;
  end;
end;



procedure TRasterImage.ReadData(Stream: TStream);
  function GetImageClass: TRasterImageClass;
  const
    // need to repeat here since they aren't defined yet
    IconSignature: array [0..3] of char = #0#0#1#0;
    CursorSignature: array [0..3] of char = #0#0#2#0;
  var
    Sig: array[0..7] of Char;
    Position: Int64;
  begin
    Position := Stream.Position;
    Stream.Read(Sig[0], SizeOf(Sig));
    Stream.Position := Position;

    if (Sig[0] = 'B') and (Sig[1] = 'M') then Exit(TBitmap);
    if CompareMem(@Sig[0], @PNGcomn.Signature[0], 8) then Exit(TPortableNetworkGraphic);
    if CompareMem(@Sig[0], @IconSignature[0], 4) then Exit(TIcon);
    if CompareMem(@Sig[0], @CursorSignature[0], 4) then Exit(TCursorImage);
    if TestStreamIsXPM(Stream) then Exit(TPixmap);

    Result := nil;
  end;

var
  Size: Longint;
  ImageClass: TRasterImageClass;
  Image: TRasterImage;
begin
  Stream.Read(Size, SizeOf(Size));
  Size := LEtoN(Size);
  
  // pre laz 0.9.26 there was no strict relation between graphic format and
  // classtype, so we need to check if we need some conversion
  if Size >= 8
  then ImageClass := GetImageClass
  else ImageClass := nil;

  if (ImageClass = nil) or ClassType.InheritsFrom(ImageClass)
  then begin
    // no conversion needed, or it wasn't a known "old" format
    LoadFromStream(Stream, Size);
    Exit;
  end;
  
  Image := ImageClass.Create;
  Image.LoadFromStream(Stream, Size);
  try
    Assign(Image);
  finally
    Image.Free;
  end;
end;

procedure TRasterImage.WriteData(Stream: TStream);
  procedure Error;
  begin
    raise FPImageException.Create(rsErrorWhileSavingBitmap);
  end;
var
  Size: Longint;
begin
  SaveStreamNeeded;
  if FSharedImage.SaveStream = nil
  then Error;

  Size := NtoLE(Longint(FSharedImage.SaveStream.Size));
  Stream.Write(Size, SizeOf(Size));
  SaveToStream(Stream);
end;

function TRasterImage.RequestTransparentColor: TColor;
var
  RawImagePtr: PRawImage;
  IntfImage: TLazIntfImage;
begin
  // if RawImage exits then use it to get pixel overwise get it from the canvas
  if Empty then
  begin
    Result := clNone;
    Exit;
  end;

  RawImagePtr := GetRawImagePtr;
  if RawImagePtr <> nil then
  begin
    IntfImage := TLazIntfImage.Create(RawImagePtr^, False);
    try
      Result := FPColorToTColor(IntfImage.Colors[0, Height - 1]);
    finally
      IntfImage.Free;
    end;
  end
  else
    Result := Canvas.GetPixel(0, Height - 1);
end;

procedure TRasterImage.SetWidth(AWidth: Integer);
begin
  SetSize(AWidth, Height);
end;

procedure TRasterImage.SetHeight(AHeight: Integer);
begin
  SetSize(Width, AHeight);
end;

// included by graphics.pp


