1 | unit BGRAOpenRaster;
|
---|
2 |
|
---|
3 | {$mode objfpc}{$H+}
|
---|
4 |
|
---|
5 | interface
|
---|
6 |
|
---|
7 | uses
|
---|
8 | Classes, SysUtils, BGRALayers, zipper, DOM, BGRABitmap, FPImage;
|
---|
9 |
|
---|
10 | const
|
---|
11 | OpenRasterMimeType = 'image/openraster'; //do not change, it's part of the file format
|
---|
12 |
|
---|
13 | type
|
---|
14 |
|
---|
15 | { TBGRAOpenRasterDocument }
|
---|
16 |
|
---|
17 | TBGRAOpenRasterDocument = class(TBGRALayeredBitmap)
|
---|
18 | private
|
---|
19 | FFiles: array of record
|
---|
20 | Filename: string;
|
---|
21 | Stream: TMemoryStream;
|
---|
22 | end;
|
---|
23 | FStackXML: TXMLDocument;
|
---|
24 | FZipInputStream: TStream;
|
---|
25 | procedure SetMimeType(AValue: string);
|
---|
26 | protected
|
---|
27 | Procedure ZipOnCreateStream(Sender : TObject; var AStream : TStream; AItem : TFullZipFileEntry);
|
---|
28 | Procedure ZipOnDoneStream(Sender : TObject; var AStream : TStream; AItem : TFullZipFileEntry);
|
---|
29 | Procedure ZipOnOpenInputStream(Sender : TObject; var AStream : TStream);
|
---|
30 | Procedure ZipOnCloseInputStream(Sender : TObject; var AStream : TStream);
|
---|
31 | procedure ClearFiles;
|
---|
32 | function GetMemoryStream(AFilename: string): TMemoryStream;
|
---|
33 | procedure SetMemoryStream(AFilename: string; AStream: TMemoryStream);
|
---|
34 | function AddLayerFromMemoryStream(ALayerFilename: string): integer;
|
---|
35 | function CopyLayerToMemoryStream(ALayerIndex: integer; ALayerFilename: string): boolean;
|
---|
36 | function CopyBitmapToMemoryStream(ABitmap: TBGRABitmap; AFilename: string): boolean;
|
---|
37 | procedure SetMemoryStreamAsString(AFilename: string; AContent: string);
|
---|
38 | function GetMemoryStreamAsString(AFilename: string): string;
|
---|
39 | procedure UnzipFromStream(AStream: TStream; AFileList: TStrings = nil);
|
---|
40 | procedure UnzipFromFile(AFilenameUTF8: string);
|
---|
41 | procedure ZipToFile(AFilenameUTF8: string);
|
---|
42 | procedure ZipToStream(AStream: TStream);
|
---|
43 | procedure CopyThumbnailToMemoryStream(AMaxWidth, AMaxHeight: integer);
|
---|
44 | procedure AnalyzeZip; virtual;
|
---|
45 | procedure PrepareZipToSave; virtual;
|
---|
46 | function GetMimeType: string; override;
|
---|
47 |
|
---|
48 | public
|
---|
49 | constructor Create; overload; override;
|
---|
50 | constructor Create(AWidth, AHeight: integer); overload; override;
|
---|
51 | procedure Clear; override;
|
---|
52 | function CheckMimeType(AStream: TStream): boolean;
|
---|
53 | procedure LoadFlatImageFromStream(AStream: TStream;
|
---|
54 | out ANbLayers: integer;
|
---|
55 | out ABitmap: TBGRABitmap);
|
---|
56 | procedure LoadFromStream(AStream: TStream); override;
|
---|
57 | procedure LoadFromFile(const filenameUTF8: string); override;
|
---|
58 | procedure SaveToFile(const filenameUTF8: string); override;
|
---|
59 | procedure SaveToStream(AStream: TStream); override;
|
---|
60 | property MimeType : string read GetMimeType write SetMimeType;
|
---|
61 | property StackXML : TXMLDocument read FStackXML;
|
---|
62 | end;
|
---|
63 |
|
---|
64 | { TFPReaderOpenRaster }
|
---|
65 |
|
---|
66 | TFPReaderOpenRaster = class(TFPCustomImageReader)
|
---|
67 | private
|
---|
68 | FWidth,FHeight,FNbLayers: integer;
|
---|
69 | protected
|
---|
70 | function InternalCheck(Stream: TStream): boolean; override;
|
---|
71 | procedure InternalRead(Stream: TStream; Img: TFPCustomImage); override;
|
---|
72 | public
|
---|
73 | property Width: integer read FWidth;
|
---|
74 | property Height: integer read FHeight;
|
---|
75 | property NbLayers: integer read FNbLayers;
|
---|
76 | end;
|
---|
77 |
|
---|
78 | { TFPWriterOpenRaster }
|
---|
79 |
|
---|
80 | TFPWriterOpenRaster = class(TFPCustomImageWriter)
|
---|
81 | protected
|
---|
82 | procedure InternalWrite (Str:TStream; Img:TFPCustomImage); override;
|
---|
83 | end;
|
---|
84 |
|
---|
85 | procedure RegisterOpenRasterFormat;
|
---|
86 |
|
---|
87 | implementation
|
---|
88 |
|
---|
89 | uses XMLRead, XMLWrite, FPReadPNG, BGRABitmapTypes, zstream, BGRAUTF8,
|
---|
90 | UnzipperExt;
|
---|
91 |
|
---|
92 | const
|
---|
93 | MergedImageFilename = 'mergedimage.png';
|
---|
94 | LayerStackFilename = 'stack.xml';
|
---|
95 |
|
---|
96 | function IsZipStream(stream: TStream): boolean;
|
---|
97 | var
|
---|
98 | header: packed array[0..1] of char;
|
---|
99 | SavePos: int64;
|
---|
100 | begin
|
---|
101 | Result := False;
|
---|
102 | try
|
---|
103 | if stream.Position + 2 < Stream.Size then
|
---|
104 | begin
|
---|
105 | header := #0#0;
|
---|
106 | SavePos := stream.Position;
|
---|
107 | stream.Read(header, 2);
|
---|
108 | stream.Position := SavePos;
|
---|
109 | if (header[0] = 'P') and (header[1] = 'K') then
|
---|
110 | Result := True;
|
---|
111 | end;
|
---|
112 | except
|
---|
113 | on ex: Exception do ;
|
---|
114 | end;
|
---|
115 | end;
|
---|
116 |
|
---|
117 | { TFPWriterOpenRaster }
|
---|
118 |
|
---|
119 | procedure TFPWriterOpenRaster.InternalWrite(Str: TStream; Img: TFPCustomImage);
|
---|
120 | var doc: TBGRAOpenRasterDocument;
|
---|
121 | tempBmp: TBGRABitmap;
|
---|
122 | x,y: integer;
|
---|
123 |
|
---|
124 | begin
|
---|
125 | doc := TBGRAOpenRasterDocument.Create;
|
---|
126 | if Img is TBGRABitmap then doc.AddLayer(Img as TBGRABitmap) else
|
---|
127 | begin
|
---|
128 | tempBmp := TBGRABitmap.Create(img.Width,img.Height);
|
---|
129 | for y := 0 to Img.Height-1 do
|
---|
130 | for x := 0 to img.Width-1 do
|
---|
131 | tempBmp.SetPixel(x,y, FPColorToBGRA(img.Colors[x,y]));
|
---|
132 | doc.AddOwnedLayer(tempBmp);
|
---|
133 | end;
|
---|
134 | doc.SaveToStream(Str);
|
---|
135 | doc.Free;
|
---|
136 | end;
|
---|
137 |
|
---|
138 | { TFPReaderOpenRaster }
|
---|
139 |
|
---|
140 | function TFPReaderOpenRaster.InternalCheck(Stream: TStream): boolean;
|
---|
141 | var magic: packed array[0..3] of byte;
|
---|
142 | OldPos,BytesRead: Int64;
|
---|
143 | doc : TBGRAOpenRasterDocument;
|
---|
144 | begin
|
---|
145 | Result:=false;
|
---|
146 | if Stream=nil then exit;
|
---|
147 | oldPos := stream.Position;
|
---|
148 | {$PUSH}{$HINTS OFF}
|
---|
149 | BytesRead := Stream.Read({%H-}magic,sizeof(magic));
|
---|
150 | {$POP}
|
---|
151 | stream.Position:= OldPos;
|
---|
152 | if BytesRead<>sizeof(magic) then exit;
|
---|
153 | if (magic[0] = $50) and (magic[1] = $4b) and (magic[2] = $03) and (magic[3] = $04) then
|
---|
154 | begin
|
---|
155 | doc := TBGRAOpenRasterDocument.Create;
|
---|
156 | result := doc.CheckMimeType(Stream);
|
---|
157 | doc.Free;
|
---|
158 | end;
|
---|
159 | end;
|
---|
160 |
|
---|
161 | procedure TFPReaderOpenRaster.InternalRead(Stream: TStream; Img: TFPCustomImage);
|
---|
162 | var
|
---|
163 | layeredImage: TBGRAOpenRasterDocument;
|
---|
164 | flat: TBGRABitmap;
|
---|
165 | x,y: integer;
|
---|
166 | begin
|
---|
167 | FWidth := 0;
|
---|
168 | FHeight:= 0;
|
---|
169 | FNbLayers:= 0;
|
---|
170 | layeredImage := TBGRAOpenRasterDocument.Create;
|
---|
171 | try
|
---|
172 | layeredImage.LoadFlatImageFromStream(Stream, FNbLayers, flat);
|
---|
173 | if Assigned(flat) then
|
---|
174 | begin
|
---|
175 | FWidth := flat.Width;
|
---|
176 | FHeight := flat.Height;
|
---|
177 | end else
|
---|
178 | begin
|
---|
179 | layeredImage.LoadFromStream(Stream);
|
---|
180 | flat := layeredImage.ComputeFlatImage;
|
---|
181 | FWidth:= layeredImage.Width;
|
---|
182 | FHeight:= layeredImage.Height;
|
---|
183 | FNbLayers:= layeredImage.NbLayers;
|
---|
184 | end;
|
---|
185 | try
|
---|
186 | if Img is TBGRACustomBitmap then
|
---|
187 | TBGRACustomBitmap(img).Assign(flat)
|
---|
188 | else
|
---|
189 | begin
|
---|
190 | Img.SetSize(flat.Width,flat.Height);
|
---|
191 | for y := 0 to flat.Height-1 do
|
---|
192 | for x := 0 to flat.Width-1 do
|
---|
193 | Img.Colors[x,y] := BGRAToFPColor(flat.GetPixel(x,y));
|
---|
194 | end;
|
---|
195 | finally
|
---|
196 | flat.free;
|
---|
197 | end;
|
---|
198 | FreeAndNil(layeredImage);
|
---|
199 | except
|
---|
200 | on ex: Exception do
|
---|
201 | begin
|
---|
202 | layeredImage.Free;
|
---|
203 | raise Exception.Create('Error while loading OpenRaster file. ' + ex.Message);
|
---|
204 | end;
|
---|
205 | end;
|
---|
206 | end;
|
---|
207 |
|
---|
208 | { TBGRAOpenRasterDocument }
|
---|
209 |
|
---|
210 | procedure TBGRAOpenRasterDocument.AnalyzeZip;
|
---|
211 | var StackStream: TMemoryStream;
|
---|
212 | imageNode, stackNode, layerNode, attr, srcAttr: TDOMNode;
|
---|
213 | i,j,w,h,idx: integer;
|
---|
214 | x,y: integer;
|
---|
215 | float: double;
|
---|
216 | errPos: integer;
|
---|
217 | opstr : string;
|
---|
218 | gammastr: string;
|
---|
219 | begin
|
---|
220 | inherited Clear;
|
---|
221 |
|
---|
222 | if MimeType <> OpenRasterMimeType then
|
---|
223 | raise Exception.Create('Invalid mime type');
|
---|
224 |
|
---|
225 | StackStream := GetMemoryStream(LayerStackFilename);
|
---|
226 | if StackStream = nil then
|
---|
227 | raise Exception.Create('Layer stack not found');
|
---|
228 |
|
---|
229 | ReadXMLFile(FStackXML, StackStream);
|
---|
230 |
|
---|
231 | imageNode := StackXML.FindNode('image');
|
---|
232 | if imagenode = nil then
|
---|
233 | raise Exception.Create('Image node not found');
|
---|
234 |
|
---|
235 | w := 0;
|
---|
236 | h := 0;
|
---|
237 | LinearBlend := true;
|
---|
238 |
|
---|
239 | if Assigned(imageNode.Attributes) then
|
---|
240 | for i:=0 to imageNode.Attributes.Length-1 do
|
---|
241 | begin
|
---|
242 | attr := imagenode.Attributes[i];
|
---|
243 | if lowercase(attr.NodeName) = 'w' then
|
---|
244 | w := strToInt(string(attr.NodeValue)) else
|
---|
245 | if lowercase(attr.NodeName) = 'h' then
|
---|
246 | h := strToInt(string(attr.NodeValue)) else
|
---|
247 | if lowercase(attr.NodeName) = 'gamma-correction' then
|
---|
248 | linearBlend := (attr.NodeValue = 'no') or (attr.NodeValue = '0');
|
---|
249 | end;
|
---|
250 |
|
---|
251 | SetSize(w,h);
|
---|
252 |
|
---|
253 | stackNode := imageNode.FindNode('stack');
|
---|
254 | if stackNode = nil then
|
---|
255 | raise Exception.Create('Stack node not found');
|
---|
256 |
|
---|
257 | for i := stackNode.ChildNodes.Length-1 downto 0 do
|
---|
258 | begin
|
---|
259 | OnLayeredBitmapLoadProgress((stackNode.ChildNodes.Length-i)*100 div stackNode.ChildNodes.Length);
|
---|
260 | layerNode:= stackNode.ChildNodes[i];
|
---|
261 | if (layerNode.NodeName = 'layer') and Assigned(layerNode.Attributes) then
|
---|
262 | begin
|
---|
263 | srcAttr := layerNode.Attributes.GetNamedItem('src');
|
---|
264 | idx := AddLayerFromMemoryStream(UTF8Encode(srcAttr.NodeValue));
|
---|
265 | if idx <> -1 then
|
---|
266 | begin
|
---|
267 | x := 0;
|
---|
268 | y := 0;
|
---|
269 | gammastr := '';
|
---|
270 | for j := 0 to layerNode.Attributes.Length-1 do
|
---|
271 | begin
|
---|
272 | attr := layerNode.Attributes[j];
|
---|
273 | if lowercase(attr.NodeName) = 'opacity' then
|
---|
274 | begin
|
---|
275 | val(attr.NodeValue, float, errPos);
|
---|
276 | if errPos = 0 then
|
---|
277 | begin
|
---|
278 | if float < 0 then float := 0;
|
---|
279 | if float > 1 then float := 1;
|
---|
280 | LayerOpacity[idx] := round(float*255);
|
---|
281 | end;
|
---|
282 | end else
|
---|
283 | if lowercase(attr.NodeName) = 'gamma-correction' then
|
---|
284 | gammastr := string(attr.NodeValue) else
|
---|
285 | if lowercase(attr.NodeName) = 'visibility' then
|
---|
286 | LayerVisible[idx] := (attr.NodeValue = 'visible') or (attr.NodeValue = 'yes') or (attr.NodeValue = '1') else
|
---|
287 | if (lowercase(attr.NodeName) = 'x') or (lowercase(attr.NodeName) = 'y') then
|
---|
288 | begin
|
---|
289 | val(attr.NodeValue, float, errPos);
|
---|
290 | if errPos = 0 then
|
---|
291 | begin
|
---|
292 | if float < -(MaxInt shr 1) then float := -(MaxInt shr 1);
|
---|
293 | if float > (MaxInt shr 1) then float := (MaxInt shr 1);
|
---|
294 | if (lowercase(attr.NodeName) = 'x') then x := round(float);
|
---|
295 | if (lowercase(attr.NodeName) = 'y') then y := round(float);
|
---|
296 | end;
|
---|
297 | end else
|
---|
298 | if lowercase(attr.NodeName) = 'name' then
|
---|
299 | LayerName[idx] := UTF8Encode(attr.NodeValue) else
|
---|
300 | if lowercase(attr.NodeName) = 'composite-op' then
|
---|
301 | begin
|
---|
302 | opstr := StringReplace(lowercase(string(attr.NodeValue)),'_','-',[rfReplaceAll]);
|
---|
303 | if (pos(':',opstr) = 0) and (opstr <> 'xor') then opstr := 'svg:'+opstr;
|
---|
304 | //parse composite op
|
---|
305 | if (opstr = 'svg:src-over') or (opstr = 'krita:dissolve') then
|
---|
306 | BlendOperation[idx] := boTransparent else
|
---|
307 | if opstr = 'svg:lighten' then
|
---|
308 | BlendOperation[idx] := boLighten else
|
---|
309 | if opstr = 'svg:screen' then
|
---|
310 | BlendOperation[idx] := boScreen else
|
---|
311 | if opstr = 'svg:color-dodge' then
|
---|
312 | BlendOperation[idx] := boColorDodge else
|
---|
313 | if (opstr = 'svg:color-burn') or (opstr = 'krita:gamma_dark'){approx} then
|
---|
314 | BlendOperation[idx] := boColorBurn else
|
---|
315 | if opstr = 'svg:darken' then
|
---|
316 | BlendOperation[idx] := boDarken else
|
---|
317 | if (opstr = 'svg:plus') or (opstr = 'svg:add') or (opstr = 'krita:linear_dodge') then
|
---|
318 | BlendOperation[idx] := boLinearAdd else
|
---|
319 | if (opstr = 'svg:multiply') or (opstr = 'krita:bumpmap') then
|
---|
320 | BlendOperation[idx] := boMultiply else
|
---|
321 | if opstr = 'svg:overlay' then
|
---|
322 | BlendOperation[idx] := boOverlay else
|
---|
323 | if opstr = 'svg:soft-light' then
|
---|
324 | BlendOperation[idx] := boSvgSoftLight else
|
---|
325 | if opstr = 'svg:hard-light' then
|
---|
326 | BlendOperation[idx] := boHardLight else
|
---|
327 | if opstr = 'svg:difference' then
|
---|
328 | BlendOperation[idx] := boLinearDifference else
|
---|
329 | if (opstr = 'krita:inverse-subtract') or (opstr = 'krita:linear-burn') then
|
---|
330 | BlendOperation[idx] := boLinearSubtractInverse else
|
---|
331 | if opstr = 'krita:subtract' then
|
---|
332 | BlendOperation[idx] := boLinearSubtract else
|
---|
333 | if (opstr = 'svg:difference') or
|
---|
334 | (opstr = 'krita:equivalence') then
|
---|
335 | BlendOperation[idx] := boLinearDifference else
|
---|
336 | if (opstr = 'svg:exclusion') or
|
---|
337 | (opstr = 'krita:exclusion') then
|
---|
338 | BlendOperation[idx] := boLinearExclusion else
|
---|
339 | if opstr = 'krita:divide' then
|
---|
340 | BlendOperation[idx] := boDivide else
|
---|
341 | if opstr = 'bgra:soft-light' then
|
---|
342 | BlendOperation[idx] := boSoftLight else
|
---|
343 | if opstr = 'bgra:nice-glow' then
|
---|
344 | BlendOperation[idx] := boNiceGlow else
|
---|
345 | if opstr = 'bgra:glow' then
|
---|
346 | BlendOperation[idx] := boGlow else
|
---|
347 | if opstr = 'bgra:reflect' then
|
---|
348 | BlendOperation[idx] := boReflect else
|
---|
349 | if opstr = 'bgra:negation' then
|
---|
350 | BlendOperation[idx] := boLinearNegation else
|
---|
351 | if (opstr = 'bgra:xor') or (opstr = 'xor') then
|
---|
352 | BlendOperation[idx] := boXor else
|
---|
353 | begin
|
---|
354 | //messagedlg('Unknown blend operation : ' + attr.NodeValue,mtInformation,[mbOk],0);
|
---|
355 | BlendOperation[idx] := boTransparent;
|
---|
356 | end;
|
---|
357 | end;
|
---|
358 | end;
|
---|
359 | LayerOffset[idx] := point(x,y);
|
---|
360 | if (gammastr = 'yes') or (gammastr = 'on') then
|
---|
361 | begin
|
---|
362 | case BlendOperation[idx] of
|
---|
363 | boLinearAdd: BlendOperation[idx] := boAdditive;
|
---|
364 | boOverlay: BlendOperation[idx] := boDarkOverlay;
|
---|
365 | boLinearDifference: BlendOperation[idx] := boDifference;
|
---|
366 | boLinearExclusion: BlendOperation[idx] := boExclusion;
|
---|
367 | boLinearSubtract: BlendOperation[idx] := boSubtract;
|
---|
368 | boLinearSubtractInverse: BlendOperation[idx] := boSubtractInverse;
|
---|
369 | boLinearNegation: BlendOperation[idx] := boNegation;
|
---|
370 | end;
|
---|
371 | end else
|
---|
372 | if (gammastr = 'no') or (gammastr = 'off') then
|
---|
373 | if BlendOperation[idx] = boTransparent then
|
---|
374 | BlendOperation[idx] := boLinearBlend; //explicit linear blending
|
---|
375 | end;
|
---|
376 | end;
|
---|
377 | end;
|
---|
378 |
|
---|
379 | end;
|
---|
380 |
|
---|
381 | procedure TBGRAOpenRasterDocument.PrepareZipToSave;
|
---|
382 | var i: integer;
|
---|
383 | imageNode,stackNode,layerNode: TDOMElement;
|
---|
384 | layerFilename,strval: string;
|
---|
385 | stackStream: TMemoryStream;
|
---|
386 | begin
|
---|
387 | ClearFiles;
|
---|
388 | MimeType := OpenRasterMimeType;
|
---|
389 | FStackXML := TXMLDocument.Create;
|
---|
390 | imageNode := TDOMElement(StackXML.CreateElement('image'));
|
---|
391 | StackXML.AppendChild(imageNode);
|
---|
392 | imageNode.SetAttribute('w',widestring(inttostr(Width)));
|
---|
393 | imageNode.SetAttribute('h',widestring(inttostr(Height)));
|
---|
394 | if LinearBlend then
|
---|
395 | imageNode.SetAttribute('gamma-correction','no')
|
---|
396 | else
|
---|
397 | imageNode.SetAttribute('gamma-correction','yes');
|
---|
398 |
|
---|
399 | stackNode := TDOMElement(StackXML.CreateElement('stack'));
|
---|
400 | imageNode.AppendChild(stackNode);
|
---|
401 | SetMemoryStreamAsString('stack.xml',''); //to put it before image data
|
---|
402 |
|
---|
403 | CopyThumbnailToMemoryStream(256,256);
|
---|
404 |
|
---|
405 | for i := NbLayers-1 downto 0 do
|
---|
406 | begin
|
---|
407 | layerFilename := 'data/layer'+inttostr(i)+'.png';
|
---|
408 | if CopyLayerToMemoryStream(i, layerFilename) then
|
---|
409 | begin
|
---|
410 | layerNode := StackXML.CreateElement('layer');
|
---|
411 | stackNode.AppendChild(layerNode);
|
---|
412 | layerNode.SetAttribute('name', UTF8Decode(LayerName[i]));
|
---|
413 | str(LayerOpacity[i]/255:0:3,strval);
|
---|
414 | layerNode.SetAttribute('opacity',widestring(strval));
|
---|
415 | layerNode.SetAttribute('src',widestring(layerFilename));
|
---|
416 | if LayerVisible[i] then
|
---|
417 | layerNode.SetAttribute('visibility','visible')
|
---|
418 | else
|
---|
419 | layerNode.SetAttribute('visibility','hidden');
|
---|
420 | layerNode.SetAttribute('x',widestring(inttostr(LayerOffset[i].x)));
|
---|
421 | layerNode.SetAttribute('y',widestring(inttostr(LayerOffset[i].y)));
|
---|
422 | strval := '';
|
---|
423 | case BlendOperation[i] of
|
---|
424 | boLighten: strval := 'svg:lighten';
|
---|
425 | boScreen: strval := 'svg:screen';
|
---|
426 | boAdditive, boLinearAdd: strval := 'svg:add';
|
---|
427 | boColorDodge: strval := 'svg:color-dodge';
|
---|
428 | boColorBurn : strval := 'svg:color-burn';
|
---|
429 | boDarken: strval := 'svg:darken';
|
---|
430 | boMultiply: strval := 'svg:multiply';
|
---|
431 | boOverlay, boDarkOverlay: strval := 'svg:overlay';
|
---|
432 | boSoftLight: strval := 'bgra:soft-light';
|
---|
433 | boHardLight: strval := 'svg:hard-light';
|
---|
434 | boDifference,boLinearDifference: strval := 'svg:difference';
|
---|
435 | boLinearSubtractInverse, boSubtractInverse: strval := 'krita:inverse_subtract';
|
---|
436 | boLinearSubtract, boSubtract: strval := 'krita:subtract';
|
---|
437 | boExclusion, boLinearExclusion: strval := 'svg:exclusion';
|
---|
438 | boDivide: strval := 'krita:divide';
|
---|
439 | boNiceGlow: strval := 'bgra:nice-glow';
|
---|
440 | boGlow: strval := 'bgra:glow';
|
---|
441 | boReflect: strval := 'bgra:reflect';
|
---|
442 | boLinearNegation,boNegation: strval := 'bgra:negation';
|
---|
443 | boXor: strval := 'bgra:xor';
|
---|
444 | boSvgSoftLight: strval := 'svg:soft-light';
|
---|
445 | else strval := 'svg:src-over';
|
---|
446 | end;
|
---|
447 | layerNode.SetAttribute('composite-op',widestring(strval));
|
---|
448 | if BlendOperation[i] <> boTransparent then //in 'transparent' case, linear blending depends on general setting
|
---|
449 | begin
|
---|
450 | if BlendOperation[i] in[boAdditive,boDarkOverlay,boDifference,boSubtractInverse,
|
---|
451 | boSubtract,boExclusion,boNegation] then
|
---|
452 | strval := 'yes' else strval := 'no';
|
---|
453 | layerNode.SetAttribute('gamma-correction',widestring(strval));
|
---|
454 | end;
|
---|
455 | end;
|
---|
456 | end;
|
---|
457 | StackStream := TMemoryStream.Create;
|
---|
458 | WriteXMLFile(StackXML, StackStream);
|
---|
459 | SetMemoryStream('stack.xml',StackStream);
|
---|
460 | end;
|
---|
461 |
|
---|
462 | procedure TBGRAOpenRasterDocument.LoadFromFile(const filenameUTF8: string);
|
---|
463 | var AStream: TFileStreamUTF8;
|
---|
464 | begin
|
---|
465 | AStream := TFileStreamUTF8.Create(filenameUTF8,fmOpenRead or fmShareDenyWrite);
|
---|
466 | try
|
---|
467 | LoadFromStream(AStream);
|
---|
468 | finally
|
---|
469 | AStream.Free;
|
---|
470 | end;
|
---|
471 | end;
|
---|
472 |
|
---|
473 | procedure TBGRAOpenRasterDocument.SaveToFile(const filenameUTF8: string);
|
---|
474 | begin
|
---|
475 | PrepareZipToSave;
|
---|
476 | ZipToFile(filenameUTF8);
|
---|
477 | ClearFiles;
|
---|
478 | end;
|
---|
479 |
|
---|
480 | procedure TBGRAOpenRasterDocument.SaveToStream(AStream: TStream);
|
---|
481 | begin
|
---|
482 | PrepareZipToSave;
|
---|
483 | ZipToStream(AStream);
|
---|
484 | ClearFiles;
|
---|
485 | end;
|
---|
486 |
|
---|
487 | function TBGRAOpenRasterDocument.GetMimeType: string;
|
---|
488 | begin
|
---|
489 | if length(FFiles)=0 then
|
---|
490 | result := OpenRasterMimeType
|
---|
491 | else
|
---|
492 | result := GetMemoryStreamAsString('mimetype');
|
---|
493 | end;
|
---|
494 |
|
---|
495 | constructor TBGRAOpenRasterDocument.Create;
|
---|
496 | begin
|
---|
497 | inherited Create;
|
---|
498 | RegisterOpenRasterFormat;
|
---|
499 | end;
|
---|
500 |
|
---|
501 | constructor TBGRAOpenRasterDocument.Create(AWidth, AHeight: integer);
|
---|
502 | begin
|
---|
503 | inherited Create(AWidth, AHeight);
|
---|
504 | RegisterOpenRasterFormat;
|
---|
505 | end;
|
---|
506 |
|
---|
507 | function TBGRAOpenRasterDocument.AddLayerFromMemoryStream(ALayerFilename: string): integer;
|
---|
508 | var stream: TMemoryStream;
|
---|
509 | bmp: TBGRABitmap;
|
---|
510 | png: TFPReaderPNG;
|
---|
511 | begin
|
---|
512 | stream := GetMemoryStream(ALayerFilename);
|
---|
513 | if stream = nil then raise Exception.Create('Layer not found');
|
---|
514 |
|
---|
515 | png := TFPReaderPNG.Create;
|
---|
516 | bmp := TBGRABitmap.Create;
|
---|
517 | try
|
---|
518 | bmp.LoadFromStream(stream,png);
|
---|
519 | except
|
---|
520 | on ex: exception do
|
---|
521 | begin
|
---|
522 | png.Free;
|
---|
523 | bmp.Free;
|
---|
524 | raise exception.Create('Layer format error');
|
---|
525 | end;
|
---|
526 | end;
|
---|
527 | png.Free;
|
---|
528 |
|
---|
529 | result := AddOwnedLayer(bmp);
|
---|
530 | LayerName[result] := ExtractFileName(ALayerFilename);
|
---|
531 | end;
|
---|
532 |
|
---|
533 | function TBGRAOpenRasterDocument.CopyLayerToMemoryStream(ALayerIndex: integer;
|
---|
534 | ALayerFilename: string): boolean;
|
---|
535 | var
|
---|
536 | bmp: TBGRABitmap;
|
---|
537 | mustFreeBmp: boolean;
|
---|
538 | p: PBGRAPixel;
|
---|
539 | n: integer;
|
---|
540 | begin
|
---|
541 | result := false;
|
---|
542 | bmp := LayerBitmap[ALayerIndex];
|
---|
543 | if bmp <> nil then mustFreeBmp := false
|
---|
544 | else
|
---|
545 | begin
|
---|
546 | bmp := GetLayerBitmapCopy(ALayerIndex);
|
---|
547 | if bmp = nil then exit;
|
---|
548 | mustFreeBmp:= true;
|
---|
549 | end;
|
---|
550 | if bmp.HasTransparentPixels then
|
---|
551 | begin
|
---|
552 | //avoid png bug with black color
|
---|
553 | if not mustFreeBmp then
|
---|
554 | begin
|
---|
555 | bmp := bmp.Duplicate as TBGRABitmap;
|
---|
556 | mustFreeBmp := true;
|
---|
557 | end;
|
---|
558 | p := bmp.data;
|
---|
559 | for n := bmp.NbPixels-1 downto 0 do
|
---|
560 | begin
|
---|
561 | if (p^.alpha <> 0) and (p^.red = 0) and (p^.green = 0) and (p^.blue = 0) then
|
---|
562 | p^.blue := 1;
|
---|
563 | inc(p);
|
---|
564 | end;
|
---|
565 | end;
|
---|
566 |
|
---|
567 | result := CopyBitmapToMemoryStream(bmp,ALayerFilename);
|
---|
568 | if mustFreeBmp then bmp.Free;
|
---|
569 | end;
|
---|
570 |
|
---|
571 | function TBGRAOpenRasterDocument.CopyBitmapToMemoryStream(ABitmap: TBGRABitmap;
|
---|
572 | AFilename: string): boolean;
|
---|
573 | var
|
---|
574 | memStream: TMemoryStream;
|
---|
575 | begin
|
---|
576 | result := false;
|
---|
577 | memstream := TMemoryStream.Create;
|
---|
578 | try
|
---|
579 | ABitmap.SaveToStreamAsPng(memStream);
|
---|
580 | SetMemoryStream(AFilename,memstream);
|
---|
581 | result := true;
|
---|
582 | except
|
---|
583 | on ex: Exception do
|
---|
584 | begin
|
---|
585 | memStream.Free;
|
---|
586 | end;
|
---|
587 | end;
|
---|
588 | end;
|
---|
589 |
|
---|
590 | procedure TBGRAOpenRasterDocument.SetMemoryStreamAsString(AFilename: string;
|
---|
591 | AContent: string);
|
---|
592 | var strstream: TStringStream;
|
---|
593 | memstream: TMemoryStream;
|
---|
594 | begin
|
---|
595 | strstream:= TStringStream.Create(AContent);
|
---|
596 | memstream := TMemoryStream.Create;
|
---|
597 | strstream.Position := 0;
|
---|
598 | memstream.CopyFrom(strstream, strstream.Size);
|
---|
599 | strstream.Free;
|
---|
600 | SetMemoryStream(AFilename, memstream);
|
---|
601 | end;
|
---|
602 |
|
---|
603 | function TBGRAOpenRasterDocument.GetMemoryStreamAsString(AFilename: string): string;
|
---|
604 | var stream: TMemoryStream;
|
---|
605 | str: TStringStream;
|
---|
606 | begin
|
---|
607 | stream := GetMemoryStream(AFilename);
|
---|
608 | str := TStringStream.Create('');
|
---|
609 | str.CopyFrom(stream,stream.Size);
|
---|
610 | result := str.DataString;
|
---|
611 | str.Free;
|
---|
612 | end;
|
---|
613 |
|
---|
614 | procedure TBGRAOpenRasterDocument.UnzipFromStream(AStream: TStream;
|
---|
615 | AFileList: TStrings = nil);
|
---|
616 | var unzip: TUnZipper;
|
---|
617 | begin
|
---|
618 | ClearFiles;
|
---|
619 | unzip := TUnZipper.Create;
|
---|
620 | try
|
---|
621 | unzip.OnCreateStream := @ZipOnCreateStream;
|
---|
622 | unzip.OnDoneStream := @ZipOnDoneStream;
|
---|
623 | unzip.OnOpenInputStream := @ZipOnOpenInputStream;
|
---|
624 | unzip.OnCloseInputStream := @ZipOnCloseInputStream;
|
---|
625 | FZipInputStream := AStream;
|
---|
626 | if Assigned(AFileList) then
|
---|
627 | begin
|
---|
628 | if AFileList.Count > 0 then
|
---|
629 | unzip.UnZipFiles(AFileList);
|
---|
630 | end else
|
---|
631 | unzip.UnZipAllFiles;
|
---|
632 | finally
|
---|
633 | FZipInputStream := nil;
|
---|
634 | unzip.Free;
|
---|
635 | end;
|
---|
636 | end;
|
---|
637 |
|
---|
638 | procedure TBGRAOpenRasterDocument.UnzipFromFile(AFilenameUTF8: string);
|
---|
639 | var unzip: TUnZipper;
|
---|
640 | begin
|
---|
641 | ClearFiles;
|
---|
642 | unzip := TUnZipper.Create;
|
---|
643 | try
|
---|
644 | unzip.FileName := Utf8ToAnsi(AFilenameUTF8);
|
---|
645 | unzip.OnCreateStream := @ZipOnCreateStream;
|
---|
646 | unzip.OnDoneStream := @ZipOnDoneStream;
|
---|
647 | unzip.UnZipAllFiles;
|
---|
648 | finally
|
---|
649 | unzip.Free;
|
---|
650 | end;
|
---|
651 | end;
|
---|
652 |
|
---|
653 | procedure TBGRAOpenRasterDocument.ZipToFile(AFilenameUTF8: string);
|
---|
654 | var
|
---|
655 | stream: TFileStreamUTF8;
|
---|
656 | begin
|
---|
657 | stream := TFileStreamUTF8.Create(AFilenameUTF8, fmCreate);
|
---|
658 | try
|
---|
659 | ZipToStream(stream);
|
---|
660 | finally
|
---|
661 | stream.Free;
|
---|
662 | end;
|
---|
663 | end;
|
---|
664 |
|
---|
665 | procedure TBGRAOpenRasterDocument.ZipToStream(AStream: TStream);
|
---|
666 | var zip: TZipper;
|
---|
667 | i: integer;
|
---|
668 | begin
|
---|
669 | zip := TZipper.Create;
|
---|
670 | try
|
---|
671 | for i := 0 to high(FFiles) do
|
---|
672 | begin
|
---|
673 | FFiles[i].Stream.Position:= 0;
|
---|
674 | zip.Entries.AddFileEntry(FFiles[i].Stream,FFiles[i].Filename).CompressionLevel := clnone;
|
---|
675 | end;
|
---|
676 | zip.SaveToStream(AStream);
|
---|
677 | finally
|
---|
678 | zip.Free;
|
---|
679 | end;
|
---|
680 | end;
|
---|
681 |
|
---|
682 | procedure TBGRAOpenRasterDocument.CopyThumbnailToMemoryStream(AMaxWidth,AMaxHeight: integer);
|
---|
683 | var thumbnail: TBGRABitmap;
|
---|
684 | w,h: integer;
|
---|
685 | begin
|
---|
686 | if (Width = 0) or (Height = 0) then exit;
|
---|
687 | thumbnail := ComputeFlatImage;
|
---|
688 | CopyBitmapToMemoryStream(thumbnail,MergedImageFilename);
|
---|
689 | if (thumbnail.Width > AMaxWidth) or
|
---|
690 | (thumbnail.Height > AMaxHeight) then
|
---|
691 | begin
|
---|
692 | if thumbnail.Width > AMaxWidth then
|
---|
693 | begin
|
---|
694 | w := AMaxWidth;
|
---|
695 | h := round(thumbnail.Height* (w/thumbnail.Width));
|
---|
696 | end else
|
---|
697 | begin
|
---|
698 | w := thumbnail.Width;
|
---|
699 | h := thumbnail.Height;
|
---|
700 | end;
|
---|
701 | if h > AMaxHeight then
|
---|
702 | begin
|
---|
703 | h := AMaxHeight;
|
---|
704 | w := round(thumbnail.Width* (h/thumbnail.Height));
|
---|
705 | end;
|
---|
706 | BGRAReplace(thumbnail, thumbnail.Resample(w,h));
|
---|
707 | end;
|
---|
708 | CopyBitmapToMemoryStream(thumbnail,'Thumbnails\thumbnail.png');
|
---|
709 | thumbnail.Free;
|
---|
710 | end;
|
---|
711 |
|
---|
712 | procedure TBGRAOpenRasterDocument.Clear;
|
---|
713 | begin
|
---|
714 | ClearFiles;
|
---|
715 | inherited Clear;
|
---|
716 | end;
|
---|
717 |
|
---|
718 | function TBGRAOpenRasterDocument.CheckMimeType(AStream: TStream): boolean;
|
---|
719 | var unzip: TUnzipperStreamUtf8;
|
---|
720 | mimeTypeFound: string;
|
---|
721 | oldPos: int64;
|
---|
722 | begin
|
---|
723 | result := false;
|
---|
724 | unzip := TUnzipperStreamUtf8.Create;
|
---|
725 | oldPos := AStream.Position;
|
---|
726 | try
|
---|
727 | unzip.InputStream := AStream;
|
---|
728 | mimeTypeFound := unzip.UnzipFileToString('mimetype');
|
---|
729 | if mimeTypeFound = OpenRasterMimeType then result := true;
|
---|
730 | except
|
---|
731 | end;
|
---|
732 | unzip.Free;
|
---|
733 | astream.Position:= OldPos;
|
---|
734 | end;
|
---|
735 |
|
---|
736 | procedure TBGRAOpenRasterDocument.LoadFlatImageFromStream(AStream: TStream; out
|
---|
737 | ANbLayers: integer; out ABitmap: TBGRABitmap);
|
---|
738 | var fileList: TStringList;
|
---|
739 | imgStream, stackStream: TMemoryStream;
|
---|
740 | imageNode, stackNode: TDOMNode;
|
---|
741 | i: integer;
|
---|
742 | begin
|
---|
743 | fileList := TStringList.Create;
|
---|
744 | fileList.Add(MergedImageFilename);
|
---|
745 | fileList.Add(LayerStackFilename);
|
---|
746 | imgStream := nil;
|
---|
747 | try
|
---|
748 | UnzipFromStream(AStream, fileList);
|
---|
749 | imgStream := GetMemoryStream(MergedImageFilename);
|
---|
750 | if imgStream = nil then
|
---|
751 | ABitmap := nil
|
---|
752 | else
|
---|
753 | ABitmap := TBGRABitmap.Create(imgStream);
|
---|
754 | ANbLayers := 1;
|
---|
755 |
|
---|
756 | stackStream := GetMemoryStream(LayerStackFilename);
|
---|
757 | ReadXMLFile(FStackXML, StackStream);
|
---|
758 | imageNode := StackXML.FindNode('image');
|
---|
759 | if Assigned(imagenode) then
|
---|
760 | begin
|
---|
761 | stackNode := imageNode.FindNode('stack');
|
---|
762 | if Assigned(stackNode) then
|
---|
763 | begin
|
---|
764 | ANbLayers:= 0;
|
---|
765 | for i := stackNode.ChildNodes.Length-1 downto 0 do
|
---|
766 | begin
|
---|
767 | if stackNode.ChildNodes[i].NodeName = 'layer' then
|
---|
768 | inc(ANbLayers);
|
---|
769 | end;
|
---|
770 | end;
|
---|
771 | end;
|
---|
772 |
|
---|
773 | finally
|
---|
774 | fileList.Free;
|
---|
775 | ClearFiles;
|
---|
776 | end;
|
---|
777 | end;
|
---|
778 |
|
---|
779 | procedure TBGRAOpenRasterDocument.LoadFromStream(AStream: TStream);
|
---|
780 | begin
|
---|
781 | OnLayeredBitmapLoadFromStreamStart;
|
---|
782 | try
|
---|
783 | UnzipFromStream(AStream);
|
---|
784 | AnalyzeZip;
|
---|
785 | finally
|
---|
786 | OnLayeredBitmapLoaded;
|
---|
787 | ClearFiles;
|
---|
788 | end;
|
---|
789 | end;
|
---|
790 |
|
---|
791 | procedure TBGRAOpenRasterDocument.SetMimeType(AValue: string);
|
---|
792 | begin
|
---|
793 | SetMemoryStreamAsString('mimetype',AValue);
|
---|
794 | end;
|
---|
795 |
|
---|
796 | procedure TBGRAOpenRasterDocument.ZipOnCreateStream(Sender: TObject; var AStream: TStream;
|
---|
797 | AItem: TFullZipFileEntry);
|
---|
798 | var MemStream: TMemoryStream;
|
---|
799 | begin
|
---|
800 | MemStream := TMemoryStream.Create;
|
---|
801 | SetMemoryStream(AItem.ArchiveFileName, MemStream);
|
---|
802 | AStream := MemStream;
|
---|
803 | end;
|
---|
804 |
|
---|
805 | {$hints off}
|
---|
806 | procedure TBGRAOpenRasterDocument.ZipOnDoneStream(Sender: TObject; var AStream: TStream;
|
---|
807 | AItem: TFullZipFileEntry);
|
---|
808 | begin
|
---|
809 | //do nothing, files stay in memory
|
---|
810 | end;
|
---|
811 | {$hints on}
|
---|
812 |
|
---|
813 | procedure TBGRAOpenRasterDocument.ZipOnOpenInputStream(Sender: TObject;
|
---|
814 | var AStream: TStream);
|
---|
815 | begin
|
---|
816 | AStream := FZipInputStream;
|
---|
817 | end;
|
---|
818 |
|
---|
819 | procedure TBGRAOpenRasterDocument.ZipOnCloseInputStream(Sender: TObject;
|
---|
820 | var AStream: TStream);
|
---|
821 | begin
|
---|
822 | AStream := nil; //avoid freeing
|
---|
823 | end;
|
---|
824 |
|
---|
825 | procedure TBGRAOpenRasterDocument.ClearFiles;
|
---|
826 | var i: integer;
|
---|
827 | begin
|
---|
828 | for i := 0 to high(FFiles) do
|
---|
829 | ffiles[i].Stream.Free;
|
---|
830 | FFiles := nil;
|
---|
831 | FreeAndNil(FStackXML);
|
---|
832 | end;
|
---|
833 |
|
---|
834 | function TBGRAOpenRasterDocument.GetMemoryStream(AFilename: string): TMemoryStream;
|
---|
835 | var i: integer;
|
---|
836 | begin
|
---|
837 | for i := 0 to high(FFiles) do
|
---|
838 | if ffiles[i].Filename = AFilename then
|
---|
839 | begin
|
---|
840 | result := FFiles[i].Stream;
|
---|
841 | result.Position:= 0;
|
---|
842 | exit;
|
---|
843 | end;
|
---|
844 | result := nil;
|
---|
845 | end;
|
---|
846 |
|
---|
847 | procedure TBGRAOpenRasterDocument.SetMemoryStream(AFilename: string;
|
---|
848 | AStream: TMemoryStream);
|
---|
849 | var i: integer;
|
---|
850 | begin
|
---|
851 | for i := 0 to high(FFiles) do
|
---|
852 | if ffiles[i].Filename = AFilename then
|
---|
853 | begin
|
---|
854 | FreeAndNil(FFiles[i].Stream);
|
---|
855 | FFiles[i].Stream := AStream;
|
---|
856 | exit;
|
---|
857 | end;
|
---|
858 | setlength(FFiles, length(FFiles)+1);
|
---|
859 | FFiles[high(FFiles)].Filename := AFilename;
|
---|
860 | FFiles[high(FFiles)].Stream := AStream;
|
---|
861 | end;
|
---|
862 |
|
---|
863 | var AlreadyRegistered: boolean;
|
---|
864 |
|
---|
865 | procedure RegisterOpenRasterFormat;
|
---|
866 | begin
|
---|
867 | if AlreadyRegistered then exit;
|
---|
868 | ImageHandlers.RegisterImageReader ('OpenRaster', 'ora', TFPReaderOpenRaster);
|
---|
869 | RegisterLayeredBitmapReader('ora', TBGRAOpenRasterDocument);
|
---|
870 | RegisterLayeredBitmapWriter('ora', TBGRAOpenRasterDocument);
|
---|
871 | //TPicture.RegisterFileFormat('ora', 'OpenRaster', TBGRAOpenRasterDocument);
|
---|
872 | DefaultBGRAImageReader[ifOpenRaster] := TFPReaderOpenRaster;
|
---|
873 | DefaultBGRAImageWriter[ifOpenRaster] := TFPWriterOpenRaster;
|
---|
874 | AlreadyRegistered:= True;
|
---|
875 | end;
|
---|
876 |
|
---|
877 | end.
|
---|
878 |
|
---|