1 // SPDX-License-Identifier: LGPL-3.0-linking-exception
2 unit BGRASVGOriginal;
3
4 {$mode objfpc}{$H+}
5
6 interface
7
8 uses
9 BGRAClasses, SysUtils, BGRABitmapTypes, BGRABitmap, BGRASVG, BGRATransform,
10 BGRALayerOriginal, BGRAUnits, BGRALayers;
11
12 type
13 TBGRALayerSVGOriginal = class;
14
15 { TBGRASVGOriginalDiff }
16
17 TBGRASVGOriginalDiff = class(TBGRAOriginalDiff)
18 protected
19 FContentVersionBefore,FContentVersionAfter: integer;
20 FSvgStreamBefore,FSvgStreamAfter: TMemoryStream;
21 FDpiBefore, FDpiAfter: single;
22 public
23 constructor Create(AFromOriginal: TBGRALayerSVGOriginal);
24 procedure ComputeDiff(AToOriginal: TBGRALayerSVGOriginal);
25 procedure Apply(AOriginal: TBGRALayerCustomOriginal); override;
26 procedure Unapply(AOriginal: TBGRALayerCustomOriginal); override;
CanAppendnull27 function CanAppend(ADiff: TBGRAOriginalDiff): boolean; override;
28 procedure Append(ADiff: TBGRAOriginalDiff); override;
IsIdentitynull29 function IsIdentity: boolean; override;
30 destructor Destroy; override;
31 end;
32
33 { TBGRALayerSVGOriginal }
34
35 TBGRALayerSVGOriginal = class(TBGRALayerCustomOriginal)
36 private
GetDPInull37 function GetDPI: single;
GetSvgHeightnull38 function GetSvgHeight: single;
GetSvgWidthnull39 function GetSvgWidth: single;
40 procedure SetDPI(AValue: single);
41 protected
42 FSVG: TBGRASVG;
43 FPresentationMatrix: TAffineMatrix;
44 FDiff: TBGRASVGOriginalDiff;
45 FContentVersion: integer;
46 procedure BeginUpdate;
47 procedure EndUpdate;
48 procedure ComputePresentation(AContainerWidth, AContainerHeight: integer; AScaleDPI: single);
49 public
50 constructor Create; override;
51 destructor Destroy; override;
52 procedure Render(ADest: TBGRABitmap; AMatrix: TAffineMatrix; ADraft: boolean); override;
GetRenderBoundsnull53 function GetRenderBounds({%H-}ADestRect: TRect; AMatrix: TAffineMatrix): TRect; override;
54 procedure LoadFromStorage(AStorage: TBGRACustomOriginalStorage); override;
55 procedure SaveToStorage(AStorage: TBGRACustomOriginalStorage); override;
56 procedure LoadFromStream(AStream: TStream); override;
57 procedure SaveSVGToStream(AStream: TStream);
58 procedure SetSVG(ASVG: TBGRASVG; AContainerWidth: integer = 640;
59 AContainerHeight: integer = 480; AScaleDPI: single = 1);
60 procedure LoadSVGFromStream(AStream: TStream; AContainerWidth: integer = 640;
61 AContainerHeight: integer = 480; AScaleDPI: single = 1);
GetSVGCopynull62 function GetSVGCopy: TBGRASVG;
StorageClassNamenull63 class function StorageClassName: RawByteString; override;
CanConvertToSVGnull64 class function CanConvertToSVG: boolean; override;
ConvertToSVGnull65 function ConvertToSVG(const AMatrix: TAffineMatrix; out AOffset: TPoint): TBGRASVG; override;
66 property Width: single read GetSvgWidth;
67 property Height: single read GetSvgHeight;
68 property DPI: single read GetDPI write SetDPI;
69 property PresentationMatrix: TAffineMatrix read FPresentationMatrix;
70 end;
71
72 { TBGRALayeredSVG }
73
74 TBGRALayeredSVG = class(TBGRALayeredBitmap)
75 protected
GetMimeTypenull76 function GetMimeType: string; override;
77 procedure InternalLoadFromStream(AStream: TStream);
78 procedure InternalSaveToStream(AStream: TStream);
79 public
80 ContainerWidth, ContainerHeight, DefaultSvgDPI, DPI: integer;
81 DefaultLayerName: string;
82 constructor Create; overload; override;
83 constructor Create(AWidth, AHeight: integer); overload; override;
84 procedure LoadFromStream(AStream: TStream); override;
85 procedure LoadFromFile(const filenameUTF8: string); override;
86 procedure SaveToStream(AStream: TStream); override;
87 procedure SaveToFile(const filenameUTF8: string); override;
88 end;
89
90 implementation
91
92 uses BGRACanvas2D, BGRAMemDirectory, BGRAUTF8, BGRASVGShapes, math, BGRASVGType,
93 BGRAVectorize;
94
95 { TBGRASVGOriginalDiff }
96
97 constructor TBGRASVGOriginalDiff.Create(AFromOriginal: TBGRALayerSVGOriginal);
98 begin
99 if Assigned(AFromOriginal.FSVG) then
100 begin
101 FSvgStreamBefore := TMemoryStream.Create;
102 AFromOriginal.FSVG.SaveToStream(FSvgStreamBefore);
103 end;
104 FContentVersionBefore:= AFromOriginal.FContentVersion;
105 FDpiBefore:= AFromOriginal.DPI;
106 end;
107
108 procedure TBGRASVGOriginalDiff.ComputeDiff(AToOriginal: TBGRALayerSVGOriginal);
109 begin
110 FreeAndNil(FSvgStreamAfter);
111 if Assigned(AToOriginal.FSVG) then
112 begin
113 FSvgStreamAfter := TMemoryStream.Create;
114 AToOriginal.FSVG.SaveToStream(FSvgStreamAfter);
115 end;
116 FContentVersionAfter:= AToOriginal.FContentVersion;
117 FDpiAfter:= AToOriginal.DPI;
118 end;
119
120 procedure TBGRASVGOriginalDiff.Apply(AOriginal: TBGRALayerCustomOriginal);
121 var
122 orig: TBGRALayerSVGOriginal;
123 begin
124 orig := AOriginal as TBGRALayerSVGOriginal;
125 if Assigned(FSvgStreamAfter) then
126 begin
127 FSvgStreamAfter.Position:= 0;
128 orig.FSVG.LoadFromStream(FSvgStreamAfter);
129 end else
130 orig.FSVG.Content.Clear;
131 orig.FContentVersion := FContentVersionAfter;
132 end;
133
134 procedure TBGRASVGOriginalDiff.Unapply(AOriginal: TBGRALayerCustomOriginal);
135 var
136 orig: TBGRALayerSVGOriginal;
137 begin
138 orig := AOriginal as TBGRALayerSVGOriginal;
139 if Assigned(FSvgStreamBefore) then
140 begin
141 FSvgStreamBefore.Position:= 0;
142 orig.FSVG.LoadFromStream(FSvgStreamBefore);
143 end else
144 orig.FSVG.Content.Clear;
145 orig.FContentVersion := FContentVersionBefore;
146 end;
147
TBGRASVGOriginalDiff.CanAppendnull148 function TBGRASVGOriginalDiff.CanAppend(ADiff: TBGRAOriginalDiff): boolean;
149 begin
150 result := (ADiff is TBGRASVGOriginalDiff) and
151 (TBGRASVGOriginalDiff(ADiff).FContentVersionAfter >= FContentVersionAfter);
152 end;
153
154 procedure TBGRASVGOriginalDiff.Append(ADiff: TBGRAOriginalDiff);
155 var
156 next: TBGRASVGOriginalDiff;
157 begin
158 next := ADiff as TBGRASVGOriginalDiff;
159 if next.FContentVersionAfter < FContentVersionAfter then
160 raise exception.Create('Cannot append diff made before this one.');
161 FDpiAfter:= next.FDpiAfter;
162 FreeAndNil(FSvgStreamAfter);
163 if Assigned(next.FSvgStreamAfter) then
164 begin
165 FSvgStreamAfter := TMemoryStream.Create;
166 next.FSvgStreamAfter.Position:= 0;
167 FSvgStreamAfter.CopyFrom(next.FSvgStreamAfter, next.FSvgStreamAfter.Size);
168 end;
169 FContentVersionAfter:= next.FContentVersionAfter;
170 end;
171
IsIdentitynull172 function TBGRASVGOriginalDiff.IsIdentity: boolean;
173 begin
174 result := (FDpiBefore = FDpiAfter) and
175 ( ((FSvgStreamBefore=nil) and (FSvgStreamAfter=nil)) or
176 (Assigned(FSvgStreamBefore) and Assigned(FSvgStreamAfter) and
177 (FSvgStreamBefore.Size = FSvgStreamAfter.Size) and
178 CompareMem(FSvgStreamBefore.Memory,FSvgStreamAfter.Memory,FSvgStreamBefore.Size)) );
179 end;
180
181 destructor TBGRASVGOriginalDiff.Destroy;
182 begin
183 FSvgStreamBefore.Free;
184 inherited Destroy;
185 end;
186
187 { TBGRALayerSVGOriginal }
188
TBGRALayerSVGOriginal.GetDPInull189 function TBGRALayerSVGOriginal.GetDPI: single;
190 begin
191 result := FSVG.DefaultDpi;
192 end;
193
TBGRALayerSVGOriginal.GetSvgHeightnull194 function TBGRALayerSVGOriginal.GetSvgHeight: single;
195 begin
196 result := FSVG.HeightAsPixel;
197 end;
198
GetSvgWidthnull199 function TBGRALayerSVGOriginal.GetSvgWidth: single;
200 begin
201 result := FSVG.WidthAsPixel;
202 end;
203
204 procedure TBGRALayerSVGOriginal.SetDPI(AValue: single);
205 begin
206 BeginUpdate;
207 FSVG.DefaultDpi:= AValue;
208 EndUpdate;
209 end;
210
211 procedure TBGRALayerSVGOriginal.BeginUpdate;
212 begin
213 if DiffExpected and (FDiff=nil) then
214 FDiff := TBGRASVGOriginalDiff.Create(self);
215 end;
216
217 procedure TBGRALayerSVGOriginal.EndUpdate;
218 begin
219 if Assigned(FDiff) then FDiff.ComputeDiff(self);
220 NotifyChange(FDiff);
221 FDiff := nil;
222 end;
223
224 procedure TBGRALayerSVGOriginal.ComputePresentation(AContainerWidth, AContainerHeight: integer;
225 AScaleDPI: single);
226 var
227 compWidth, compHeight: single;
228 begin
229 FSVG.Units.ContainerWidth := FloatWithCSSUnit(AContainerWidth / AScaleDPI, cuPixel);
230 FSVG.Units.ContainerHeight := FloatWithCSSUnit(AContainerHeight / AScaleDPI, cuPixel);
231 compWidth := FSVG.WidthAsPixel;
232 compHeight := FSVG.HeightAsPixel;
233 FSVG.WidthAsPixel := compWidth * AScaleDPI;
234 FSVG.HeightAsPixel := compHeight * AScaleDPI;
235 FPresentationMatrix := FSVG.GetStretchPresentationMatrix(cuPixel);
236 end;
237
238 constructor TBGRALayerSVGOriginal.Create;
239 begin
240 inherited Create;
241 FSVG := TBGRASVG.Create;
242 FContentVersion := 0;
243 end;
244
245 destructor TBGRALayerSVGOriginal.Destroy;
246 begin
247 FSVG.Free;
248 FDiff.Free;
249 inherited Destroy;
250 end;
251
252 procedure TBGRALayerSVGOriginal.Render(ADest: TBGRABitmap;
253 AMatrix: TAffineMatrix; ADraft: boolean);
254 var
255 c2D: TBGRACanvas2D;
256 begin
257 if Assigned(FSVG) then
258 begin
259 c2D := TBGRACanvas2D.Create(ADest);
260 c2D.transform(AMatrix*FPresentationMatrix);
261 c2D.fontRenderer := TBGRAVectorizedFontRenderer.Create;
262 if ADraft then c2D.antialiasing := false;
263 FSVG.Draw(c2D, 0, 0, cuPixel);
264 c2D.Free;
265 end;
266 end;
267
GetRenderBoundsnull268 function TBGRALayerSVGOriginal.GetRenderBounds(ADestRect: TRect;
269 AMatrix: TAffineMatrix): TRect;
270 var
271 aff: TAffineBox;
272 r: TRectF;
273 begin
274 if Assigned(FSVG) then
275 begin
276 with FSVG.ViewBox do
277 r := RectWithSizeF(min.x, min.y, size.x, size.y);
278 aff := AMatrix * FPresentationMatrix * TAffineBox.AffineBox(r);
279 result := aff.RectBounds;
280 end else
281 result := EmptyRect;
282 end;
283
284 procedure TBGRALayerSVGOriginal.LoadFromStorage(
285 AStorage: TBGRACustomOriginalStorage);
286 var svgStream: TMemoryStream;
287 valDpi: Single;
288 begin
289 svgStream := TMemoryStream.Create;
290 try
291 if AStorage.ReadFile('content.svg', svgStream) then
292 begin
293 if not Assigned(FSVG) then FSVG := TBGRASVG.Create;
294 svgStream.Position:= 0;
295 FSVG.LoadFromStream(svgStream);
296 end else
297 begin
298 FreeAndNil(FSVG);
299 FSVG := TBGRASVG.Create;
300 end;
301 FPresentationMatrix := FSVG.GetStretchPresentationMatrix(cuPixel);
302 FContentVersion:= AStorage.Int['content-version'];
303 finally
304 svgStream.Free;
305 end;
306 valDpi := AStorage.Float['dpi'];
307 if valDpi <> EmptySingle then
308 FSVG.DefaultDpi:= valDpi;
309 end;
310
311 procedure TBGRALayerSVGOriginal.SaveToStorage(
312 AStorage: TBGRACustomOriginalStorage);
313 var svgStream: TMemoryStream;
314 begin
315 if Assigned(FSVG) then
316 begin
317 if FContentVersion > AStorage.Int['content-version'] then
318 begin
319 svgStream := TMemoryStream.Create;
320 try
321 FSVG.SaveToStream(svgStream);
322 AStorage.WriteFile('content.svg', svgStream, true, true);
323 svgStream := nil;
324 AStorage.Int['content-version'] := FContentVersion;
325 finally
326 svgStream.Free;
327 end;
328 end;
329 AStorage.Float['dpi'] := FSVG.DefaultDpi;
330 end;
331 end;
332
333 procedure TBGRALayerSVGOriginal.LoadFromStream(AStream: TStream);
334 begin
335 if TMemDirectory.CheckHeader(AStream) then
336 inherited LoadFromStream(AStream)
337 else
338 LoadSVGFromStream(AStream);
339 end;
340
341 procedure TBGRALayerSVGOriginal.SaveSVGToStream(AStream: TStream);
342 begin
343 FSVG.SaveToStream(AStream);
344 end;
345
346 procedure TBGRALayerSVGOriginal.SetSVG(ASVG: TBGRASVG; AContainerWidth: integer;
347 AContainerHeight: integer; AScaleDPI: single);
348 begin
349 BeginUpdate;
350 FSVG.Free;
351 FSVG := ASVG;
352 ComputePresentation(AContainerWidth, AContainerHeight, AScaleDPI);
353 Inc(FContentVersion);
354 EndUpdate;
355 end;
356
357 procedure TBGRALayerSVGOriginal.LoadSVGFromStream(AStream: TStream; AContainerWidth: integer;
358 AContainerHeight: integer; AScaleDPI: single);
359 begin
360 BeginUpdate;
361 FSVG.LoadFromStream(AStream);
362 ComputePresentation(AContainerWidth, AContainerHeight, AScaleDPI);
363 Inc(FContentVersion);
364 EndUpdate;
365 end;
366
GetSVGCopynull367 function TBGRALayerSVGOriginal.GetSVGCopy: TBGRASVG;
368 begin
369 result := FSVG.Duplicate;
370 end;
371
TBGRALayerSVGOriginal.StorageClassNamenull372 class function TBGRALayerSVGOriginal.StorageClassName: RawByteString;
373 begin
374 result := 'svg';
375 end;
376
TBGRALayerSVGOriginal.CanConvertToSVGnull377 class function TBGRALayerSVGOriginal.CanConvertToSVG: boolean;
378 begin
379 Result:= true;
380 end;
381
TBGRALayerSVGOriginal.ConvertToSVGnull382 function TBGRALayerSVGOriginal.ConvertToSVG(const AMatrix: TAffineMatrix; out AOffset: TPoint): TBGRASVG;
383 begin
384 if not IsAffineMatrixIdentity(AMatrix) then
385 raise exception.Create('Matrix not valid for SVG original');
386 AOffset := Point(0,0);
387 Result:= GetSVGCopy;
388 end;
389
390 { TBGRALayeredSVG }
391
TBGRALayeredSVG.GetMimeTypenull392 function TBGRALayeredSVG.GetMimeType: string;
393 begin
394 Result:= 'image/svg+xml';
395 end;
396
397 procedure TBGRALayeredSVG.InternalLoadFromStream(AStream: TStream);
398 var
399 svg, svgLayer: TBGRASVG;
400 visualWidth, visualHeight: single;
401 svgOrig: TBGRALayerSVGOriginal;
402 idx, i, j: Integer;
403 layer: TSVGGroup;
404 prefix: String;
405 originalViewBox: TSVGViewBox;
406 m: TAffineMatrix;
407 begin
408 svg := TBGRASVG.Create;
409 try
410 svg.DefaultDpi := DefaultSvgDPI;
411 svg.LoadFromStream(AStream);
412 svg.Units.ContainerWidth := FloatWithCSSUnit(ContainerWidth / DPI * svg.DefaultDpi, cuPixel);
413 svg.Units.ContainerHeight := FloatWithCSSUnit(ContainerHeight / DPI * svg.DefaultDpi, cuPixel);
414 svg.CropToViewBox(DPI / svg.DefaultDpi);
415 visualWidth := svg.WidthAsPixel;
416 visualHeight := svg.HeightAsPixel;
417 Clear;
418 SetSize(floor(visualWidth + 0.95),floor(visualHeight + 0.95));
419 if svg.LayerCount > 0 then
420 begin
421 for i := 0 to svg.LayerCount-1 do
422 begin
423 layer := svg.Layer[i];
424 svgLayer := TBGRASVG.Create;
425 svgLayer.DefaultDpi:= svg.DefaultDpi;
426 svgLayer.WidthAsPixel := visualWidth;
427 svgLayer.HeightAsPixel := visualHeight;
428 for j := 0 to svg.NamespaceCount-1 do
429 begin
430 prefix := svg.NamespacePrefix[j];
431 svgLayer.NamespaceURI[prefix] := svg.NamespaceURI[prefix];
432 end;
433 try
434 svgLayer.ViewBox := svg.ViewBox;
435 if layer.DOMElement.hasAttribute('bgra:originalViewBox') then
436 begin
437 originalViewBox := TSVGViewBox.Parse(layer.DOMElement.GetAttribute('bgra:originalViewBox'));
438 svgLayer.WidthAsPixel := originalViewBox.size.x;
439 svgLayer.HeightAsPixel := originalViewBox.size.y;
440 svgLayer.ViewBox := originalViewBox;
441 m := layer.matrix[cuPixel] * AffineMatrixTranslation(originalViewBox.min.x, originalViewBox.min.y);
442 end else
443 m := layer.matrix[cuPixel];
444 for j := 0 to svg.Content.IndexOfElement(layer)-1 do
445 if svg.Content.ElementObject[j] is TSVGDefine then
446 svgLayer.Content.CopyElement(svg.Content.ElementObject[j]);
447 for j := 0 to layer.Content.ElementCount-1 do
448 svgLayer.Content.CopyElement(layer.Content.ElementObject[j]);
449 svgOrig := TBGRALayerSVGOriginal.Create;
450 svgOrig.SetSVG(svgLayer);
451 svgLayer := nil;
452 idx := AddLayerFromOwnedOriginal(svgOrig);
453 LayerName[idx] := layer.Name;
454 LayerVisible[idx] := layer.Visible;
455 LayerOpacity[idx] := min(255,max(0,round(layer.opacity*255)));
456 BlendOperation[idx] := layer.mixBlendMode;
457 LayerOriginalMatrix[idx] := m;
458 RenderLayerFromOriginal(idx);
459 finally
460 svgLayer.Free;
461 end;
462 end;
463 end else
464 begin
465 svgOrig := TBGRALayerSVGOriginal.Create;
466 svgOrig.SetSVG(svg);
467 svg := nil;
468 idx := AddLayerFromOwnedOriginal(svgOrig);
469 LayerName[idx] := DefaultLayerName+'1';
470 RenderLayerFromOriginal(idx);
471 end;
472 finally
473 svg.Free;
474 end;
475 end;
476
477 procedure TBGRALayeredSVG.InternalSaveToStream(AStream: TStream);
478
479 procedure StoreLayerBitmap(ABitmap: TBGRABitmap; AOwned: boolean; const AMatrix: TAffineMatrix;
480 ADestElem: TSVGCustomElement; AContent: TSVGContent);
481 var
482 img: TSVGImage;
483 ab: TAffineBox;
484 vb: TSVGViewBox;
485 begin
486 ab := AMatrix * TAffineBox.AffineBox(PointF(0, 0), PointF(ABitmap.Width, 0), PointF(0, ABitmap.Height));
487
488 img := AContent.AppendImage(AMatrix[1,3], AMatrix[2, 3], ABitmap.Width, ABitmap.Height, ABitmap, AOwned);
489 img.matrix[cuCustom] := AffineMatrixLinear(AMatrix);
490
491 if ADestElem is TSVGGroup then
492 with TSVGGroup(ADestElem) do
493 begin
494 with ab.RectBounds do
495 begin
496 vb.min := PointF(Left, Top);
497 vb.size := PointF(Width, Height);
498 end;
499 DOMElement.SetAttribute('xmlns:bgra', 'https://wiki.freepascal.org/LazPaint_SVG_format');
500 DOMElement.SetAttribute('bgra:originalViewBox', vb.ToString);
501 end;
502 end;
503
504 procedure StoreLayer(ALayerIndex: integer; ASVG: TBGRASVG; ADestElem: TSVGCustomElement;
505 ADest: TSVGContent; out AMatrix: TAffineMatrix);
506 var
507 c: TBGRALayerOriginalAny;
508 bmp, part: TBGRABitmap;
509 layerSvg: TBGRASVG;
510 i: Integer;
511 prefix: String;
512 origViewBox: TSVGViewBox;
513 wantedOfs: TPoint;
514 r: TRect;
515 begin
516 AMatrix := AffineMatrixIdentity;
517 if LayerOriginalKnown[ALayerIndex] then
518 c:= LayerOriginalClass[ALayerIndex]
519 else c := nil;
520
521 if Assigned(c) and c.CanConvertToSVG then
522 begin
523 if LayerOriginal[ALayerIndex].IsInfiniteSurface then
524 begin
525 layerSvg := LayerOriginal[ALayerIndex].ConvertToSVG(LayerOriginalMatrix[ALayerIndex],
526 wantedOfs) as TBGRASVG;
527 layerSvg.WidthAsPixel:= Self.Width;
528 layerSvg.HeightAsPixel:= Self.Height;
529 AMatrix := AffineMatrixTranslation(wantedOfs.X, wantedOfs.Y);
530 end else
531 begin
532 layerSvg := LayerOriginal[ALayerIndex].ConvertToSVG(AffineMatrixIdentity,
533 wantedOfs) as TBGRASVG;
534 AMatrix:= LayerOriginalMatrix[ALayerIndex]
535 * AffineMatrixTranslation(wantedOfs.X, wantedOfs.Y)
536 * layerSvg.GetStretchPresentationMatrix(cuPixel);
537 end;
538 origViewBox := layerSvg.ViewBox;
539 try
540 layerSvg.ConvertToUnit(cuCustom);
541 if ADestElem is TSVGGroup then
542 with TSVGGroup(ADestElem) do
543 begin
544 DOMElement.SetAttribute('xmlns:bgra', 'https://wiki.freepascal.org/LazPaint_SVG_format');
545 DOMElement.SetAttribute('bgra:originalViewBox', origViewBox.ToString);
546 end;
547 for i := 0 to layerSvg.Content.ElementCount-1 do
548 ADest.CopyElement(layerSvg.Content.ElementObject[i]);
549 for i := 0 to layerSvg.NamespaceCount-1 do
550 begin
551 prefix := layerSvg.NamespacePrefix[i];
552 ASVG.NamespaceURI[prefix] := layerSvg.NamespaceURI[prefix];
553 end;
554 finally
555 layerSvg.Free;
556 end;
557 end else
558 begin
559 r := LayerBitmap[ALayerIndex].GetImageBounds;
560 if (r.Left = 0) and (r.Top = 0) and (r.Width = LayerBitmap[ALayerIndex].Width) and
561 (r.Height = LayerBitmap[ALayerIndex].Height) then
562 StoreLayerBitmap(LayerBitmap[ALayerIndex], false,
563 AffineMatrixTranslation(LayerOffset[ALayerIndex].X, LayerOffset[ALayerIndex].Y), ADestElem, ADest)
564 else
565 begin
566 part := LayerBitmap[ALayerIndex].GetPart(r);
567 StoreLayerBitmap(part, true,
568 AffineMatrixTranslation(LayerOffset[ALayerIndex].X + r.Left,
569 LayerOffset[ALayerIndex].Y + r.Top), ADestElem, ADest)
570 end;
571 end;
572 end;
573
574 var
575 svg: TBGRASVG;
576 vb : TSVGViewBox;
577 i,j: Integer;
578 g: TSVGGroup;
579 m: TAffineMatrix;
580 identifiers, newIdentifiers: TStringList;
581 begin
582 svg := TBGRASVG.Create;
583 identifiers := nil;
584 newIdentifiers := nil;
585 try
586 svg.WidthAsPixel := Width;
587 svg.HeightAsPixel := Height;
588 vb.min := PointF(0, 0);
589 vb.size := PointF(Width, Height);
590 svg.ViewBox := vb;
591 if (NbLayers = 1) and (LayerOpacity[0] = 255) and LayerVisible[0] and
592 (LayerOriginalGuid[0] = GUID_NULL) then
593 begin
594 StoreLayer(0, svg, svg, svg.Content, m);
595 end else
596 begin
597 svg.NamespaceURI['inkscape'] := 'http://www.inkscape.org/namespaces/inkscape';
598 identifiers := TStringList.Create;
599 newIdentifiers := TStringList.Create;
600 for i := 0 to NbLayers-1 do
601 begin
602 g := svg.Content.AppendGroup;
603 g.IsLayer := true;
604 g.Name:= LayerName[i];
605 g.opacity:= LayerOpacity[i]/255;
606 g.Visible:= LayerVisible[i];
607 g.mixBlendMode:= BlendOperation[i];
608 StoreLayer(i, svg, g, g.Content, m);
609 g.matrix[cuPixel] := m;
610 identifiers.Clear;
611 g.ListIdentifiers(identifiers);
612 newIdentifiers.Clear;
613 for j := 0 to identifiers.Count-1 do
614 newIdentifiers.Add('layer'+inttostr(i+1)+'-'+identifiers[j]);
615 g.RenameIdentifiers(identifiers, newIdentifiers);
616 end;
617 end;
618 svg.SaveToStream(AStream);
619 finally
620 newIdentifiers.Free;
621 identifiers.Free;
622 svg.Free;
623 end;
624 end;
625
626 constructor TBGRALayeredSVG.Create;
627 begin
628 inherited Create;
629 ContainerWidth:= 640;
630 ContainerHeight:= 480;
631 DefaultLayerName := 'Layer';
632 DPI := 96;
633 DefaultSvgDPI:= 96;
634 end;
635
636 constructor TBGRALayeredSVG.Create(AWidth, AHeight: integer);
637 begin
638 inherited Create(AWidth, AHeight);
639 ContainerWidth:= 640;
640 ContainerHeight:= 480;
641 DefaultLayerName := 'Layer';
642 end;
643
644 procedure TBGRALayeredSVG.LoadFromStream(AStream: TStream);
645 begin
646 OnLayeredBitmapLoadFromStreamStart;
647 try
648 InternalLoadFromStream(AStream);
649 finally
650 OnLayeredBitmapLoaded;
651 end;
652 end;
653
654 procedure TBGRALayeredSVG.LoadFromFile(const filenameUTF8: string);
655 var AStream: TFileStreamUTF8;
656 begin
657 AStream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
658 OnLayeredBitmapLoadStart(filenameUTF8);
659 try
660 LoadFromStream(AStream);
661 finally
662 OnLayeredBitmapLoaded;
663 AStream.Free;
664 end;
665 end;
666
667 procedure TBGRALayeredSVG.SaveToStream(AStream: TStream);
668 begin
669 OnLayeredBitmapSaveToStreamStart;
670 try
671 InternalSaveToStream(AStream);
672 finally
673 OnLayeredBitmapSaved;
674 end;
675 end;
676
677 procedure TBGRALayeredSVG.SaveToFile(const filenameUTF8: string);
678 var AStream: TFileStreamUTF8;
679 begin
680 AStream := TFileStreamUTF8.Create(filenameUTF8,fmCreate);
681 OnLayeredBitmapSaveStart(filenameUTF8);
682 try
683 InternalSaveToStream(AStream);
684 finally
685 OnLayeredBitmapSaved;
686 AStream.Free;
687 end;
688 end;
689
690 procedure RegisterLayeredSvgFormat;
691 begin
692 RegisterLayeredBitmapReader('svg', TBGRALayeredSVG);
693 RegisterLayeredBitmapWriter('svg', TBGRALayeredSVG);
694 end;
695
696 initialization
697
698 RegisterLayerOriginal(TBGRALayerSVGOriginal);
699 RegisterLayeredSvgFormat;
700
701 end.
702
703