1{%MainUnit ../graphics.pp}
2
3{******************************************************************************
4                                     TRasterImage
5 ******************************************************************************
6
7 *****************************************************************************
8  This file is part of the Lazarus Component Library (LCL)
9
10  See the file COPYING.modifiedLGPL.txt, included in this distribution,
11  for details about the license.
12 *****************************************************************************
13}
14
15procedure TRasterImage.Assign(Source: TPersistent);
16  procedure CopyMask(AMask: HBITMAP);
17  var
18    RI: TRawImage;
19    msk, dummy: HBITMAP;
20    Res: Boolean;
21  begin
22    // we need a winapi.CopyImage here (would make things easier)
23    // in theory, it should not matter if a HBITMAP was created as bitmap or as mask
24    // since there is a description problem in gtk, create both (we cannot create mask only)
25    // todo: fix gtk
26    if not RawImage_FromBitmap(RI, AMask, AMask) then Exit;
27    msk := 0;
28    dummy := 0;
29    RawImage_CreateBitmaps(RI, dummy, msk {, True});
30    RI.FreeData;
31    DeleteObject(dummy);
32
33    if BitmapHandleAllocated
34    then Res := UpdateHandles(BitmapHandle, msk)
35    else Res := UpdateHandles(0, msk);
36    if not Res then DeleteObject(msk);
37  end;
38
39
40var
41  SrcImage: TRasterImage absolute Source;
42  SrcFPImage: TFPCustomImage absolute Source;
43  SrcRawImage, DstRawImage: PRawImage;
44  IntfImage: TLazIntfImage;
45  ImgHandle,ImgMaskHandle: HBitmap;
46begin
47  if Source = Self then exit;
48
49  if Source is TRasterImage
50  then begin
51    if MaskHandleAllocated
52    then begin
53      // Clear mask first mask
54      if BitmapHandleAllocated
55      then UpdateHandles(BitmapHandle, 0)
56      else UpdateHandles(0, 0);
57    end;
58
59    FTransparentMode := SrcImage.FTransparentMode;
60    FTransparentColor := SrcImage.FTransparentColor;
61    FMasked := SrcImage.FMasked;
62
63    // -> check if already shared
64    if SrcImage.FSharedImage <> FSharedImage
65    then begin
66      // image is not shared => new image data
67      // -> free canvas (interface handles)
68      FreeCanvasContext;
69      // release old FImage
70      FSharedImage.Release;
71
72      // We only can share images of the same type ...
73      if CanShareImage(SrcImage.GetSharedImageClass)
74      then begin
75        // share FImage with assigned graphic
76        FSharedImage := SrcImage.FSharedImage;
77        FSharedImage.Reference;
78
79        // when updating, unshare
80        // Since we "share" it first, the unshare code will create a copy
81        if (FUpdateCount > 0)
82        or (SrcImage.FUpdateCount > 0)
83        then begin
84          UnshareImage(True);
85          FreeSaveStream;
86        end;
87      end
88      else begin
89        // not sharable, create rawimage copy
90        FSharedImage := GetSharedImageClass.Create;
91        FSharedImage.Reference;
92
93        // copy raw image
94        SrcRawImage := SrcImage.GetRawImagePtr;
95        DstRawImage := GetRawImagePtr;
96        if (SrcRawImage <> nil) and (DstRawImage <> nil)
97        then with SrcRawImage^ do
98          ExtractRect(Rect(0, 0, Description.Width, Description.Height), DstRawImage^);
99      end;
100    end;
101
102    if SrcImage.MaskHandleAllocated
103    then CopyMask(SrcImage.MaskHandle);
104
105
106    if FUpdateCount = 0
107    then Changed(Self);
108
109    Exit;
110  end;
111
112  if Source is TFPCustomImage
113  then begin
114    // todo: base on rawimage
115
116    IntfImage := TLazIntfImage.Create(0,0,[]);
117    try
118      if BitmapHandleAllocated
119      then IntfImage.DataDescription := GetDescriptionFromBitmap(BitmapHandle, 0, 0)
120      else IntfImage.DataDescription := GetDescriptionFromDevice(0, 0, 0);
121      IntfImage.Assign(SrcFPImage);
122      IntfImage.CreateBitmaps(ImgHandle, ImgMaskHandle);
123      SetHandles(ImgHandle, ImgMaskHandle);
124    finally
125      IntfImage.Free;
126    end;
127    if FUpdateCount = 0
128    then Changed(Self);
129
130    Exit;
131  end;
132
133  // fall back to default
134  inherited Assign(Source);
135end;
136
137procedure TRasterImage.BeginUpdate(ACanvasOnly: Boolean);
138begin
139  if FUpdateCount = 0
140  then begin
141    UnshareImage(True);
142    FUpdateCanvasOnly := ACanvasOnly;
143  end
144  else begin
145    // if we are updating all, then requesting a canvas only won't change it
146    // if we are updating canvas only, then requesting all is an error
147    if FUpdateCanvasOnly and not ACanvasOnly
148    then raise EInvalidGraphicOperation.Create(rsRasterImageUpdateAll);
149  end;
150
151  Inc(FUpdateCount);
152end;
153
154procedure TRasterImage.Clear;
155begin
156  if Empty then Exit;
157
158  FreeSaveStream;
159  SetSize(0, 0);
160  if FUpdateCount = 0
161  then Changed(Self);
162end;
163
164procedure TRasterImage.BitmapHandleNeeded;
165var
166  ImgHandle, ImgMaskHandle: HBitmap;
167  ImagePtr: PRawImage;
168  DevDesc: TRawImageDescription;
169  QueryFlags: TRawImageQueryFlags;
170  W, H: Integer;
171  SkipMask: Boolean;
172begin
173  if BitmapHandleAllocated then exit;
174
175  ImagePtr := GetRawImagePtr;
176  if ImagePtr = nil then Exit;
177
178  ImgMaskHandle := 0;
179
180  // we must skip mask creation if
181  // a) we already have mask
182  // b) mask needs to be created another way - using TransparentColor
183  // c) there's no mask data
184  SkipMask := MaskHandleAllocated
185           or (TransparentMode = tmFixed)
186           or not ImagePtr^.IsMasked(False);
187
188  if not CreateCompatibleBitmaps(ImagePtr^, ImgHandle, ImgMaskHandle, SkipMask)
189  then begin
190    {$IFNDEF DisableChecks}
191    DebugLn('TRasterImage.BitmapHandleNeeded: Unable to create handles, using default');
192    {$ENDIF}
193    // create a default handle
194    W := Width;
195    if W < 1 then W := 1;
196    H := Height;
197    if H < 1 then H := 1;
198
199    if ImagePtr^.Description.Depth = 1
200    then QueryFlags := [riqfMono]
201    else QueryFlags := [riqfRGB];
202    if ImagePtr^.Description.AlphaPrec <> 0
203    then Include(QueryFlags, riqfAlpha);
204    if ImagePtr^.Description.MaskBitsPerPixel <> 0
205    then Include(QueryFlags, riqfMask);
206    QueryDescription(DevDesc, QueryFlags, W, H);
207    ImgHandle := CreateDefaultBitmapHandle(DevDesc);
208  end;
209
210  // if we do not have new mask then either use old one or use none
211  if SkipMask and MaskHandleAllocated
212  then UpdateHandles(ImgHandle, MaskHandle)
213  else UpdateHandles(ImgHandle, ImgMaskHandle);
214end;
215
216function TRasterImage.CanShareImage(AClass: TSharedRasterImageClass): Boolean;
217begin
218  // We only can share images of the same type.
219  // And if we are some "end" graphic type
220  Result := (AClass <> TSharedCustomBitmap)
221        and (AClass = GetSharedImageClass);
222end;
223
224
225procedure TRasterImage.Draw(DestCanvas: TCanvas; const DestRect: TRect);
226var
227  UseMaskHandle: HBitmap;
228  SrcDC: hDC;
229  DestDC: hDC;
230begin
231  if (Width=0) or (Height=0)
232  then Exit;
233
234  BitmapHandleNeeded;
235  if not BitmapHandleAllocated then Exit;
236
237  if Masked then
238    UseMaskHandle:=MaskHandle
239  else
240    UseMaskHandle:=0;
241
242  SrcDC := Canvas.GetUpdatedHandle([csHandleValid]);
243  DestCanvas.Changing;
244  DestDC := DestCanvas.GetUpdatedHandle([csHandleValid]);
245  StretchMaskBlt(DestDC,
246          DestRect.Left,DestRect.Top,
247          DestRect.Right-DestRect.Left,DestRect.Bottom-DestRect.Top,
248          SrcDC,0,0,Width,Height, UseMaskHandle,0,0,DestCanvas.CopyMode);
249  DestCanvas.Changed;
250end;
251
252procedure TRasterImage.EndUpdate(AStreamIsValid: Boolean = False);
253begin
254  if FUpdatecount = 0
255  then raise EInvalidGraphicOperation.Create(rsRasterImageEndUpdate);
256
257  Dec(FUpdatecount);
258  if FUpdatecount > 0 then Exit;
259
260  if not FUpdateCanvasOnly
261  then begin
262    FreeCanvasContext;
263    // delete bitmaphandle too
264    if BitmapHandleAllocated
265    then DeleteObject(InternalReleaseBitmapHandle)
266
267  end;
268
269  if not AStreamIsValid
270  then FreeSaveStream;
271  Changed(Self);
272end;
273
274constructor TRasterImage.Create;
275begin
276  inherited Create;
277  FSharedImage := GetSharedImageClass.Create;
278  FSharedImage.Reference;
279  FTransparentColor := clDefault; // for Delphi compatibility. clDefault means:
280                                  // use Left,Bottom pixel as transparent pixel
281end;
282
283destructor TRasterImage.Destroy;
284begin
285  FreeCanvasContext;
286  FSharedImage.Release;
287  FSharedImage := nil;
288  FreeAndNil(FCanvas);
289  inherited Destroy;
290end;
291
292procedure TRasterImage.FreeCanvasContext;
293begin
294  if (FCanvas <> nil) then TBitmapCanvas(FCanvas).FreeDC;
295end;
296
297function TRasterImage.GetCanvas: TCanvas;
298begin
299  if FCanvas = nil then
300    CreateCanvas;
301  Result := FCanvas;
302end;
303
304procedure TRasterImage.CreateCanvas;
305begin
306  if FCanvas <> nil then Exit;
307
308  FCanvas := TBitmapCanvas.Create(Self);
309  FCanvas.OnChanging := @CanvasChanging;
310  FCanvas.OnChange := @Changed;
311end;
312
313procedure TRasterImage.FreeImage;
314begin
315  SetHandle(0);
316end;
317
318procedure TRasterImage.LoadFromBitmapHandles(ABitmap, AMask: HBitmap; ARect: PRect);
319var
320  RawImg: TRawImage;
321  ImgHandle, ImgMaskHandle: HBitmap;
322begin
323  //DebugLn('TRasterImage.CreateFromBitmapHandles A SrcRect=',dbgs(SrcRect));
324  if not RawImage_FromBitmap(RawImg, ABitmap, AMask, ARect) then
325    raise EInvalidGraphicOperation.Create('TRasterImage.LoadFromBitmapHandles Get RawImage');
326  ImgHandle:=0;
327  ImgMaskHandle:=0;
328  try
329    //DebugLn('TRasterImage.CreateFromBitmapHandles B SrRect=',dbgs(SrcRect));
330    if not RawImage_CreateBitmaps(RawImg, ImgHandle, ImgMaskHandle) then
331      raise EInvalidGraphicOperation.Create('TRasterImage.LoadFromBitmapHandles Create bitmaps');
332    SetHandles(ImgHandle, ImgMaskHandle);
333    ImgHandle:=0;
334    ImgMaskHandle:=0;
335  finally
336    RawImg.FreeData;
337    if ImgHandle<>0 then DeleteObject(ImgHandle);
338    if ImgMaskHandle<>0 then DeleteObject(ImgMaskHandle);
339  end;
340end;
341
342procedure TRasterImage.LoadFromDevice(DC: HDC);
343var
344  IntfImg: TLazIntfImage;
345  ImgHandle, ImgMaskHandle: HBitmap;
346begin
347  ImgHandle:=0;
348  ImgMaskHandle:=0;
349  IntfImg:=nil;
350  try
351    // create the interface image
352    IntfImg:=TLazIntfImage.Create(0,0,[]);
353    // get a snapshot
354    IntfImg.LoadFromDevice(DC);
355    // create HBitmap
356    IntfImg.CreateBitmaps(ImgHandle, ImgMaskHandle);
357    // feed HBitmap into a TRasterImage
358    SetHandles(ImgHandle, ImgMaskHandle);
359    ImgHandle:=0;
360    ImgMaskHandle:=0;
361  finally
362    IntfImg.Free;
363    if ImgHandle<>0 then DeleteObject(ImgHandle);
364    if ImgMaskHandle<>0 then DeleteObject(ImgMaskHandle);
365  end;
366end;
367
368procedure TRasterImage.Mask(ATransparentColor: TColor);
369begin
370  CreateMask(ATransparentColor);
371end;
372
373procedure TRasterImage.SetTransparentColor(AValue: TColor);
374begin
375  if FTransparentColor = AValue then exit;
376  FTransparentColor := AValue;
377
378  if AValue = clDefault
379  then FTransparentMode := tmAuto
380  else FTransparentMode := tmFixed;
381
382  if MaskHandleAllocated
383  then MaskHandle := 0
384  else Changed(Self);
385end;
386
387procedure TRasterImage.Changed(Sender: TObject);
388begin
389  if FUpdateCount > 0 then Exit;
390
391  //FMaskBitsValid := False;
392  if Sender = FCanvas
393  then FreeSaveStream;
394
395  inherited Changed(Sender);
396end;
397
398function TRasterImage.CreateDefaultBitmapHandle(
399  const ADesc: TRawImageDescription): HBITMAP;
400begin
401  Result := 0;
402end;
403
404procedure TRasterImage.CanvasChanging(Sender: TObject);
405begin
406  if FUpdateCount > 0 then Exit;
407
408  // called before the canvas is modified
409  // -> make sure the handle is unshared (otherwise the modifications will also
410  //    modify all copies)
411  // -> Savestream will be freed when changed (so it can be loaded by canvas)
412  UnshareImage(True);
413end;
414
415procedure TRasterImage.LoadFromStream(AStream: TStream);
416begin
417  LoadFromStream(AStream, AStream.Size - AStream.Position);
418end;
419
420procedure TRasterImage.LoadFromStream(AStream: TStream; ASize: Cardinal);
421var
422  WorkStream: TMemoryStream;
423  OldPos, NewSize: Int64;
424begin
425  BeginUpdate;
426  UnshareImage(False);
427  Clear; // clear old saved stream, allocated handles, etc
428  if ASize = 0
429  then begin
430    EndUpdate(False);
431    Exit;
432  end;
433
434  WorkStream := nil;
435  try
436    WorkStream := TMemoryStream.Create;
437    WorkStream.SetSize(ASize);
438    OldPos := AStream.Position;
439    WorkStream.CopyFrom(AStream, ASize);
440    WorkStream.Position := 0;
441    ReadStream(WorkStream, ASize);
442    NewSize := WorkStream.Position;
443    if NewSize < ASize
444    then begin
445      // the size given was different than the image loaded
446      // MWE: original code adjusted the streampos, do we stil need this ?
447      AStream.Position := OldPos + NewSize;
448      WorkStream.SetSize(NewSize);
449    end;
450    // Store our worstream as savestream
451    FSharedImage.SaveStream := WorkStream;
452    WorkStream := nil;
453  finally
454    // if something went wrong, free the workstream
455    WorkStream.Free;
456  end;
457  EndUpdate(True);
458end;
459
460function TRasterImage.GetRawImage: TRawImage;
461var
462  p: PRawImage;
463begin
464  p := GetRawImagePtr;
465  if p = nil
466  then Result{%H-}.Init
467  else Result := p^;
468end;
469
470function TRasterImage.GetScanline(ARow: Integer): Pointer;
471var
472  RI: TRawImage;
473begin
474  RI := GetRawImage;
475  Result := RI.GetLineStart(ARow);
476end;
477
478function TRasterImage.GetTransparentColor: TColor;
479begin
480  if FTransparentColor = clDefault
481  then Result := RequestTransparentColor
482  else Result := FTransparentColor;
483end;
484
485procedure TRasterImage.GetSupportedSourceMimeTypes(List: TStrings);
486begin
487  if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TCustomIcon) then
488  begin
489    List.Clear;
490    List.Add(PredefinedClipboardMimeTypes[pcfBitmap]);
491    List.Add(PredefinedClipboardMimeTypes[pcfPixmap]);
492  end else
493    inherited GetSupportedSourceMimeTypes(List);
494end;
495
496function TRasterImage.GetTransparent: Boolean;
497var
498  Desc: PRawImageDescription;
499begin
500  if Masked
501  then begin
502    // postpone description generation since we know we are transparent here
503    Result := True
504  end
505  else begin
506    Desc := GetRawImageDescriptionPtr;
507    Result := (Desc <> nil)
508          and (Desc^.Format <> ricfNone)
509          and (Desc^.AlphaPrec > 0);
510    //TODO:  check for transparency through palette etc.
511  end;
512end;
513
514function TRasterImage.GetWidth: Integer;
515var
516  Desc: PRawImageDescription;
517begin
518  Desc := GetRawImageDescriptionPtr;
519  if (Desc = nil) or (Desc^.Format = ricfNone)
520  then Result := 0
521  else Result := Desc^.Width;
522end;
523
524function TRasterImage.HandleAllocated: boolean;
525begin
526  Result := FSharedImage.FHandle <> 0;
527end;
528
529function TRasterImage.GetMimeType: string;
530begin
531  {$IFDEF VerboseLCLTodos}{$note: implement}{$ENDIF}
532
533  (*
534  if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TCustomIcon) then begin
535    if FSharedImage.SaveStream<>nil then begin
536      case FSharedImage.SaveStreamType of
537      bnXPixmap: Result:=PredefinedClipboardMimeTypes[pcfPixmap];
538      else
539        Result:=PredefinedClipboardMimeTypes[pcfBitmap];
540      end;
541    end else
542      Result:=PredefinedClipboardMimeTypes[pcfBitmap];
543  end else
544  *)
545
546  Result:=inherited GetMimeType;
547end;
548
549procedure TRasterImage.LoadFromIntfImage(IntfImage: TLazIntfImage);
550var
551  ImgHandle, ImgMaskHandle: HBitmap;
552begin
553  IntfImage.CreateBitmaps(ImgHandle, ImgMaskHandle, not IntfImage.HasMask);
554  SetHandles(ImgHandle, ImgMaskHandle);
555end;
556
557procedure TRasterImage.FreeSaveStream;
558begin
559  if FSharedImage.FSaveStream = nil then exit;
560  //DebugLn(['TRasterImage.FreeSaveStream A ',ClassName,' ',FImage.FSaveStream.Size]);
561  UnshareImage(false);
562  FreeAndNil(FSharedImage.FSaveStream);
563end;
564
565procedure TRasterImage.LoadFromMimeStream(AStream: TStream; const AMimeType: string);
566begin
567  {$IFDEF VerboseLCLTodos}{$note Make oo}{$ENDIF}
568  if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TCustomIcon) then begin
569    if (AnsiCompareText(AMimeType,PredefinedClipboardMimeTypes[pcfBitmap])=0)
570    or (AnsiCompareText(AMimeType,PredefinedClipboardMimeTypes[pcfPixmap])=0) then
571    begin
572      LoadFromStream(AStream);
573      exit;
574    end;
575  end;
576  inherited LoadFromMimeStream(AStream, AMimeType);
577end;
578
579procedure TRasterImage.LoadFromRawImage(const AImage: TRawImage; ADataOwner: Boolean);
580var
581  img: PRawImage;
582begin
583  BeginUpdate;
584  try
585    Clear;
586    if AImage.Description.Format = ricfNone then Exit; // empty image
587
588    img := GetRawImagePtr;
589
590    // Make sure, we do not free AImage.Data or Mask
591    if img^.Data = AImage.Data then begin
592      img^.Data := nil;
593      img^.DataSize := 0;
594    end;
595    if img^.Mask = AImage.Mask then begin
596      img^.Mask := nil;
597      img^.MaskSize := 0;
598    end;
599    if img^.Palette = AImage.Palette then begin
600      img^.Palette := nil;
601      img^.PaletteSize := 0;
602    end;
603    img^.FreeData;
604
605    img^.Description := AImage.Description;
606    if ADataOwner
607    then begin
608      img^.DataSize := AImage.DataSize;
609      img^.Data := AImage.Data;
610      img^.MaskSize := AImage.MaskSize;
611      img^.Mask := AImage.Mask;
612      img^.PaletteSize := AImage.PaletteSize;
613      img^.Palette := AImage.Palette;
614    end
615    else begin
616      // copy needed
617      img^.DataSize := AImage.DataSize;
618      if img^.DataSize > 0
619      then begin
620        GetMem(img^.Data, img^.DataSize);
621        Move(AImage.Data^, img^.Data^, img^.DataSize);
622      end
623      else img^.Data := nil;
624
625      img^.MaskSize := AImage.MaskSize;
626      if img^.MaskSize > 0
627      then begin
628        GetMem(img^.Mask, img^.MaskSize);
629        Move(AImage.Mask^, img^.Mask^, img^.MaskSize);
630      end
631      else img^.Mask := nil;
632
633      img^.PaletteSize := AImage.PaletteSize;
634      if img^.PaletteSize > 0
635      then begin
636        GetMem(img^.Palette, img^.PaletteSize);
637        Move(AImage.Palette^, img^.Palette^, img^.PaletteSize);
638      end
639      else img^.Palette := nil;
640    end;
641  finally
642    EndUpdate;
643  end;
644end;
645
646procedure TRasterImage.SaveToStream(AStream: TStream);
647  procedure Error;
648  begin
649    raise FPImageException.Create(rsErrorWhileSavingBitmap);
650  end;
651var
652  Size, BytesWritten: Int64;
653begin
654  SaveStreamNeeded;
655  if FSharedImage.SaveStream = nil
656  then Error;
657
658  FSharedImage.SaveStream.Position := 0;
659  Size := FSharedImage.SaveStream.Size;
660  if AStream is TMemoryStream
661  then TMemoryStream(AStream).SetSize(AStream.Position + Size);
662
663  BytesWritten := AStream.CopyFrom(FSharedImage.SaveStream, Size);
664  if BytesWritten <> Size
665  then Error;
666end;
667
668procedure TRasterImage.SetBitmapHandle(AValue: HBITMAP);
669begin
670  if MaskHandleAllocated
671  then SetHandles(AValue, MaskHandle)
672  else SetHandles(AValue, 0);
673end;
674
675procedure TRasterImage.SetHandle(AValue: THandle);
676begin
677  if FSharedImage.FHandle = AValue then Exit;
678  FreeCanvasContext;
679  UnshareImage(False);
680  FSharedImage.FHandle := AValue;
681  if FUpdateCount = 0
682  then Changed(Self);
683end;
684
685procedure TRasterImage.SetMaskHandle(AValue: HBITMAP);
686begin
687  if BitmapHandleAllocated
688  then SetHandles(BitmapHandle, AValue)
689  else SetHandles(0, AValue);
690end;
691
692procedure TRasterImage.SetMasked(AValue: Boolean);
693begin
694  if AValue = Masked then Exit;
695
696  FMasked := AValue;
697  Changed(Self);
698end;
699
700procedure TRasterImage.SetTransparentMode(AValue: TTransparentMode);
701begin
702  if AValue = TransparentMode then exit;
703  FTransparentMode := AValue;
704
705  if AValue = tmAuto
706  then TransparentColor := clDefault
707  else TransparentColor := RequestTransparentColor;
708end;
709
710procedure TRasterImage.SetTransparent(AValue: Boolean);
711var
712  lTransparent: Boolean;
713begin
714  lTransparent := GetTransparent();
715  if AValue = lTransparent then Exit;
716
717  // some delphi compatibility, we can only change transparency through the mask.
718  Masked := AValue;
719end;
720
721// release handles without freeing them
722// useful for creating a HBitmap
723function TRasterImage.ReleaseBitmapHandle: HBITMAP;
724begin
725  BitmapHandleNeeded;
726  FreeCanvasContext;
727  Result := InternalReleaseBitmapHandle;
728end;
729
730function TRasterImage.ReleaseMaskHandle: HBITMAP;
731begin
732  MaskHandleNeeded;
733  FreeCanvasContext;
734  Result := InternalReleaseMaskHandle;
735end;
736
737function TRasterImage.ReleasePalette: HPALETTE;
738begin
739  PaletteNeeded;
740  FreeCanvasContext;
741  Result := InternalReleasePalette;
742end;
743
744procedure TRasterImage.SaveStreamNeeded;
745var
746  WorkStream: TMemoryStream;
747begin
748  if FUpdateCount > 0
749  then raise EInvalidGraphicOperation.Create(rsRasterImageSaveInUpdate);
750
751  if FSharedImage.SaveStream <> nil then Exit;
752
753  WorkStream := TMemoryStream.Create;
754  try
755    WriteStream(WorkStream);
756    // Store our workstream as savestream
757    FSharedImage.SaveStream := WorkStream;
758    WorkStream := nil;
759  finally
760    // if something went wrong, free the workstream
761    WorkStream.Free;
762  end;
763end;
764
765function TRasterImage.CreateIntfImage: TLazIntfImage;
766begin
767  {$IFDEF VerboseLCLTodos}{$note todo: create based on rawimage}{$ENDIF}
768  Result := TLazIntfImage.Create(0,0,[]);
769  Result.LoadFromBitmap(BitmapHandle, MaskHandle);
770end;
771
772procedure TRasterImage.CreateMask(AColor: TColor);
773var
774  IntfImage: TLazIntfImage;
775  ImgHandle, MskHandle: HBitmap;
776  TransColor: TFPColor;
777begin
778  //DebugLn(['TRasterImage.CreateMask ',Width,'x',Height,' ',Transparent,' ',dbgs(ord(TransparentMode)),' ',dbgs(TransparentColor)]);
779  if (Width = 0)
780  or (Height = 0)
781  or (AColor = clNone)
782  or (  (FTransparentMode = tmFixed)
783    and (FTransparentColor = clNone)
784    and (AColor = clDefault)
785  )
786  then begin
787    MaskHandle := 0;
788    Exit;
789  end;
790
791  {$IFDEF VerboseLCLTodos}{$note todo: move to IntfImage}{$ENDIF}
792  IntfImage := TLazIntfImage.Create(0,0,[]);
793  try
794    // force handle creation here, since at next step we will check for mask handle
795    ImgHandle := BitmapHandle;
796    // load from bitmap needs a mask handle otherwise no mask description is
797    // created.
798    if MaskHandleAllocated
799    then MskHandle := MaskHandle
800    else MskHandle := CreateBitmap(Width, Height, 1, 1, nil);
801    IntfImage.LoadFromBitmap(ImgHandle, MskHandle);
802    if not MaskHandleAllocated
803    then DeleteObject(MskHandle);
804    ImgHandle := 0;
805
806    if AColor = clDefault
807    then begin
808      if (FTransparentMode = tmFixed) and (FTransparentColor <> clDefault)
809      then TransColor := TColorToFPColor(ColorToRGB(FTransparentColor))
810      else TransColor := IntfImage.Colors[0, IntfImage.Height - 1];
811    end
812    else TransColor := TColorToFPColor(ColorToRGB(AColor));
813
814    IntfImage.Mask(TransColor);
815
816    IntfImage.CreateBitmaps(ImgHandle, MskHandle);
817    MaskHandle := MskHandle;
818    DeleteObject(ImgHandle);
819  finally
820    IntfImage.Free;
821  end;
822end;
823
824function TRasterImage.GetEmpty: boolean;
825begin
826  Result := FSharedImage.IsEmpty;
827end;
828
829function TRasterImage.GetHandle: THandle;
830begin
831  HandleNeeded;
832  Result := FSharedImage.FHandle;
833end;
834
835function TRasterImage.GetHeight: Integer;
836var
837  Desc: PRawImageDescription;
838begin
839  Desc := GetRawImageDescriptionPtr;
840  if (Desc = nil) or (Desc^.Format = ricfNone)
841  then Result := 0
842  else Result := Desc^.Height;
843end;
844
845function TRasterImage.GetMasked: Boolean;
846begin
847  Result := FMasked;
848end;
849
850class function TRasterImage.GetSharedImageClass: TSharedRasterImageClass;
851begin
852  Result := TSharedRasterImage;
853end;
854
855procedure TRasterImage.GetSize(out AWidth, AHeight: Integer);
856var
857  Desc: PRawImageDescription;
858begin
859  Desc := GetRawImageDescriptionPtr;
860  if (Desc = nil) or (Desc^.Format = ricfNone)
861  then begin
862    AWidth := 0;
863    AHeight := 0;
864  end
865  else begin
866    AWidth := Desc^.Width;
867    AHeight := Desc^.Height;
868  end;
869end;
870
871
872
873procedure TRasterImage.ReadData(Stream: TStream);
874  function GetImageClass: TRasterImageClass;
875  const
876    // need to repeat here since they aren't defined yet
877    IconSignature: array [0..3] of char = #0#0#1#0;
878    CursorSignature: array [0..3] of char = #0#0#2#0;
879  var
880    Sig: array[0..7] of Char;
881    Position: Int64;
882  begin
883    Position := Stream.Position;
884    Stream.Read(Sig[0], SizeOf(Sig));
885    Stream.Position := Position;
886
887    if (Sig[0] = 'B') and (Sig[1] = 'M') then Exit(TBitmap);
888    if CompareMem(@Sig[0], @PNGcomn.Signature[0], 8) then Exit(TPortableNetworkGraphic);
889    if CompareMem(@Sig[0], @IconSignature[0], 4) then Exit(TIcon);
890    if CompareMem(@Sig[0], @CursorSignature[0], 4) then Exit(TCursorImage);
891    if TestStreamIsXPM(Stream) then Exit(TPixmap);
892
893    Result := nil;
894  end;
895
896var
897  Size: Longint;
898  ImageClass: TRasterImageClass;
899  Image: TRasterImage;
900begin
901  Stream.Read(Size, SizeOf(Size));
902  Size := LEtoN(Size);
903
904  // pre laz 0.9.26 there was no strict relation between graphic format and
905  // classtype, so we need to check if we need some conversion
906  if Size >= 8
907  then ImageClass := GetImageClass
908  else ImageClass := nil;
909
910  if (ImageClass = nil) or ClassType.InheritsFrom(ImageClass)
911  then begin
912    // no conversion needed, or it wasn't a known "old" format
913    LoadFromStream(Stream, Size);
914    Exit;
915  end;
916
917  Image := ImageClass.Create;
918  Image.LoadFromStream(Stream, Size);
919  try
920    Assign(Image);
921  finally
922    Image.Free;
923  end;
924end;
925
926procedure TRasterImage.WriteData(Stream: TStream);
927  procedure Error;
928  begin
929    raise FPImageException.Create(rsErrorWhileSavingBitmap);
930  end;
931var
932  Size: Longint;
933begin
934  SaveStreamNeeded;
935  if FSharedImage.SaveStream = nil
936  then Error;
937
938  Size := NtoLE(Longint(FSharedImage.SaveStream.Size));
939  Stream.Write(Size, SizeOf(Size));
940  SaveToStream(Stream);
941end;
942
943function TRasterImage.RequestTransparentColor: TColor;
944var
945  RawImagePtr: PRawImage;
946  IntfImage: TLazIntfImage;
947begin
948  // if RawImage exits then use it to get pixel overwise get it from the canvas
949  if Empty then
950  begin
951    Result := clNone;
952    Exit;
953  end;
954
955  RawImagePtr := GetRawImagePtr;
956  if RawImagePtr <> nil then
957  begin
958    IntfImage := TLazIntfImage.Create(RawImagePtr^, False);
959    try
960      Result := FPColorToTColor(IntfImage.Colors[0, Height - 1]);
961    finally
962      IntfImage.Free;
963    end;
964  end
965  else
966    Result := Canvas.GetPixel(0, Height - 1);
967end;
968
969procedure TRasterImage.SetWidth(AWidth: Integer);
970begin
971  SetSize(AWidth, Height);
972end;
973
974procedure TRasterImage.SetHeight(AHeight: Integer);
975begin
976  SetSize(Width, AHeight);
977end;
978
979// included by graphics.pp
980
981
982