markus / ConvertService / ConverterService / ImageFields / BitmapTransformer.cs @ 8c128693
이력 | 보기 | 이력해설 | 다운로드 (44.8 KB)
1 |
using System; |
---|---|
2 |
using System.Collections.Generic; |
3 |
using System.Linq; |
4 |
using System.Text; |
5 |
using System.Windows; |
6 |
using System.Windows.Media.Imaging; |
7 |
using System.Windows.Media; |
8 |
using System.Collections.ObjectModel; |
9 |
using System.Diagnostics; |
10 |
using System.Security.Permissions; |
11 |
using System.Windows.Threading; |
12 |
using System.IO; |
13 |
|
14 |
namespace ImageFields |
15 |
{ |
16 |
internal class BitmapTransformer |
17 |
{ |
18 |
// Fields |
19 |
private WriteableBitmap bitmap; |
20 |
private KeyedCollection<string, CachedFrame> cachedFrames = new CachedFrameCollection(); |
21 |
private KeyedCollection<string, InputNode> inputs = new InputNodeCollection(); |
22 |
private int largeFile = 0x5f5e100; |
23 |
private KeyedCollection<string, OutputNode> outputs = new OutputNodeCollection(); |
24 |
private byte[] pixelStorage; |
25 |
public const int TargetDpi = 0x60; |
26 |
private const double Tolerance = 1E-10; |
27 |
private int totalBlank; |
28 |
private int totalComposite; |
29 |
private int totalOutputs; |
30 |
private int totalSingle; |
31 |
|
32 |
// Events |
33 |
public event EventHandler<StreamEventArgs> InputCompleted; |
34 |
|
35 |
public event EventHandler<StreamEventArgs> InputNeeded; |
36 |
|
37 |
public event EventHandler<StreamEventArgs> OutputCompleted; |
38 |
|
39 |
public event EventHandler<StreamEventArgs> OutputNeeded; |
40 |
|
41 |
// Methods |
42 |
public BitmapTransformer() |
43 |
{ |
44 |
this.OutputAlpha = true; |
45 |
this.KeepImagesInCache = true; |
46 |
this.StripingEnabled = true; |
47 |
} |
48 |
|
49 |
public void AddInput(string fileName) |
50 |
{ |
51 |
if (!this.inputs.Contains(fileName)) |
52 |
{ |
53 |
this.inputs.Add(new InputNode(fileName)); |
54 |
} |
55 |
} |
56 |
|
57 |
public void AddOutput(string fileName, int width, int height, double quality, ImageFormat format, Color backgroundColor, bool cacheFrame) |
58 |
{ |
59 |
if (!this.outputs.Contains(fileName)) |
60 |
{ |
61 |
OutputNode item = new OutputNode(fileName) |
62 |
{ |
63 |
BackgroundColor = backgroundColor, |
64 |
Width = width, |
65 |
Height = height, |
66 |
Quality = quality, |
67 |
Format = format, |
68 |
CacheFrame = cacheFrame |
69 |
}; |
70 |
this.outputs.Add(item); |
71 |
this.totalOutputs++; |
72 |
} |
73 |
} |
74 |
|
75 |
public void AddOutputAsInput(string fileName) |
76 |
{ |
77 |
if (!this.inputs.Contains(fileName) && this.cachedFrames.Contains(fileName)) |
78 |
{ |
79 |
InputNode node = new InputNode(fileName); |
80 |
|
81 |
node.InputSource = this.cachedFrames[fileName].frame; |
82 |
node. PixelHeight = node.InputSource.PixelHeight; |
83 |
node.PixelWidth = node.InputSource.PixelWidth; |
84 |
node.LastScanLineLoaded = node.PixelHeight; |
85 |
this.inputs.Add(node); |
86 |
} |
87 |
} |
88 |
|
89 |
public void AddTransformation(string input, string output, Rect inputRect, Rect outputRect) |
90 |
{ |
91 |
Debug.Assert(!inputRect.IsEmpty, "Empty input rect"); |
92 |
Debug.Assert(!outputRect.IsEmpty, "Empty output rect"); |
93 |
Debug.Assert((inputRect.X >= 0.0) && (inputRect.Y >= 0.0), "Negative input rect"); |
94 |
Debug.Assert((outputRect.X >= 0.0) && (outputRect.Y >= 0.0), "Negative output rect"); |
95 |
if ((outputRect.Width > 0.5) && (outputRect.Height > 0.5)) |
96 |
{ |
97 |
Int32Rect rect = RectToInt32Rect(inputRect); |
98 |
if ((inputRect.Width > 0.0) && (rect.Width == 0)) |
99 |
{ |
100 |
rect.Width = 1; |
101 |
rect.X = (int)Math.Floor(inputRect.X); |
102 |
} |
103 |
if ((inputRect.Height > 0.0) && (rect.Height == 0)) |
104 |
{ |
105 |
rect.Height = 1; |
106 |
rect.Y = (int)Math.Floor(inputRect.Y); |
107 |
} |
108 |
if ((rect.Width > 0) && (rect.Height > 0)) |
109 |
{ |
110 |
TransformationNode item = new TransformationNode |
111 |
{ |
112 |
Input = input, |
113 |
InputRect = rect, |
114 |
OutputRect = outputRect |
115 |
}; |
116 |
this.outputs[output].Transforms.Add(item); |
117 |
} |
118 |
} |
119 |
} |
120 |
|
121 |
internal PixelFormat ChoosePixelFormat(ImageFormat imageFormat) |
122 |
{ |
123 |
if (((imageFormat == ImageFormat.Png) || (imageFormat == ImageFormat.Wdp)) && this.OutputAlpha) |
124 |
{ |
125 |
return PixelFormats.Bgra32; |
126 |
} |
127 |
return PixelFormats.Bgr24; |
128 |
} |
129 |
|
130 |
public void CleanUp() |
131 |
{ |
132 |
this.CloseAllInputs(); |
133 |
this.cachedFrames.Clear(); |
134 |
this.outputs.Clear(); |
135 |
GC.Collect(2); |
136 |
GC.Collect(2); |
137 |
} |
138 |
|
139 |
public void CloseAllInputs() |
140 |
{ |
141 |
foreach (InputNode node in this.inputs) |
142 |
{ |
143 |
this.CloseInput(node.FileName); |
144 |
} |
145 |
this.bitmap = null; |
146 |
this.pixelStorage = null; |
147 |
} |
148 |
|
149 |
public void CloseInput(string fileName) |
150 |
{ |
151 |
InputNode node = this.inputs[fileName]; |
152 |
node.InputSource = null; |
153 |
node.FirstScanLineLoaded = 0; |
154 |
node.LastScanLineLoaded = 0; |
155 |
} |
156 |
|
157 |
[FileIOPermission(SecurityAction.LinkDemand)] |
158 |
public void CloseOutput(string fileName) |
159 |
{ |
160 |
if (this.outputs.Contains(fileName)) |
161 |
{ |
162 |
this.CloseOutputImpl(this.outputs[fileName]); |
163 |
this.outputs.Remove(fileName); |
164 |
} |
165 |
} |
166 |
|
167 |
[FileIOPermission(SecurityAction.LinkDemand)] |
168 |
private void CloseOutputImpl(OutputNode outputNode) |
169 |
{ |
170 |
PixelFormat format; |
171 |
BitmapSource source; |
172 |
string str; |
173 |
DrawingVisual visual; |
174 |
DrawingContext context; |
175 |
RenderTargetBitmap bitmap2; |
176 |
FormatConvertedBitmap bitmap3; |
177 |
WriteableBitmap bitmap4; |
178 |
BitmapEncoder encoder = this.GetEncoder(outputNode, out format, out str); |
179 |
Rect rectangle = new Rect(0.0, 0.0, (double)outputNode.Width, (double)outputNode.Height); |
180 |
BitmapMetadata destinationMD = null; |
181 |
if (this.CopyMetadata) |
182 |
{ |
183 |
string containerFormat = string.Empty; |
184 |
switch (outputNode.Format) |
185 |
{ |
186 |
case ImageFormat.Jpg: |
187 |
containerFormat = "jpg"; |
188 |
break; |
189 |
|
190 |
case ImageFormat.Png: |
191 |
containerFormat = "png"; |
192 |
break; |
193 |
|
194 |
case ImageFormat.Wdp: |
195 |
containerFormat = "wmphoto"; |
196 |
break; |
197 |
} |
198 |
destinationMD = new BitmapMetadata(containerFormat); |
199 |
} |
200 |
bool flag = true; |
201 |
if (((IsFormatOpaque(format) && (outputNode.BackgroundColor.R == Colors.Black.R)) && ((outputNode.BackgroundColor.G == Colors.Black.G) && (outputNode.BackgroundColor.B == Colors.Black.B))) || (!IsFormatOpaque(format) && (outputNode.BackgroundColor.A == Colors.Transparent.A))) |
202 |
{ |
203 |
flag = false; |
204 |
} |
205 |
if (outputNode.Transforms.Count <= 0) |
206 |
{ |
207 |
if (flag) |
208 |
{ |
209 |
bitmap2 = new RenderTargetBitmap(outputNode.Width, outputNode.Height, 96.0, 96.0, PixelFormats.Default); |
210 |
visual = new DrawingVisual(); |
211 |
using (context = visual.RenderOpen()) |
212 |
{ |
213 |
context.DrawRectangle(new SolidColorBrush(outputNode.BackgroundColor), null, rectangle); |
214 |
} |
215 |
bitmap2.Render(visual); |
216 |
bitmap3 = new FormatConvertedBitmap(bitmap2, format, null, 0.0); |
217 |
source = bitmap3; |
218 |
} |
219 |
else |
220 |
{ |
221 |
bitmap4 = new WriteableBitmap(outputNode.Width, outputNode.Height, 96.0, 96.0, format, null); |
222 |
source = bitmap4; |
223 |
} |
224 |
this.totalBlank++; |
225 |
} |
226 |
else |
227 |
{ |
228 |
TransformationNode node = null; |
229 |
Rect outputRect; |
230 |
Int32Rect rect3; |
231 |
BitmapSource source2; |
232 |
Int32Rect inputRect; |
233 |
InputNode node2; |
234 |
BitmapSource source4; |
235 |
BitmapMetadata metadata; |
236 |
bool flag2 = true; |
237 |
for (int i = outputNode.Transforms.Count - 1; i > 0; i--) |
238 |
{ |
239 |
node = outputNode.Transforms[i]; |
240 |
if (node.OutputRect == rectangle) |
241 |
{ |
242 |
rect3 = new Int32Rect(0, 0, 0, 0); |
243 |
if (IsFormatOpaque(this.CreateInputFrame(this.inputs[node.Input], rect3).Format)) |
244 |
{ |
245 |
outputNode.Transforms.RemoveRange(0, i); |
246 |
break; |
247 |
} |
248 |
} |
249 |
} |
250 |
if (outputNode.Transforms.Count != 1) |
251 |
{ |
252 |
flag2 = false; |
253 |
} |
254 |
if (flag2) |
255 |
{ |
256 |
node = outputNode.Transforms[0]; |
257 |
rect3 = new Int32Rect(0, 0, 0, 0); |
258 |
BitmapSource source3 = this.CreateInputFrame(this.inputs[node.Input], rect3); |
259 |
if ((source3.DpiX != 96.0) || (source3.DpiY != 96.0)) |
260 |
{ |
261 |
flag2 = false; |
262 |
} |
263 |
if (!RectEqualWithTolerance(node.OutputRect, rectangle)) |
264 |
{ |
265 |
flag2 = false; |
266 |
} |
267 |
if (!(!(outputNode.BackgroundColor != Colors.Transparent) || IsFormatOpaque(source3.Format))) |
268 |
{ |
269 |
flag2 = false; |
270 |
} |
271 |
} |
272 |
if (flag2) |
273 |
{ |
274 |
node = outputNode.Transforms[0]; |
275 |
outputRect = node.OutputRect; |
276 |
inputRect = node.InputRect; |
277 |
node2 = this.inputs[node.Input]; |
278 |
source2 = this.CreateInputFrame(node2, inputRect); |
279 |
if (!this.KeepImagesInCache) |
280 |
{ |
281 |
this.CloseInput(node.Input); |
282 |
} |
283 |
inputRect.Y -= node2.FirstScanLineLoaded; |
284 |
source4 = ScaleAndCropBitmap(ref outputRect, ref inputRect, source2); |
285 |
if (source2.Format != format) |
286 |
{ |
287 |
source4 = new FormatConvertedBitmap(source4, format, null, 0.0); |
288 |
} |
289 |
if (this.CopyMetadata) |
290 |
{ |
291 |
metadata = (BitmapMetadata)source2.Metadata; |
292 |
if (metadata != null) |
293 |
{ |
294 |
CopyBitmapMetadata(metadata, destinationMD); |
295 |
} |
296 |
} |
297 |
source = source4; |
298 |
this.totalSingle++; |
299 |
} |
300 |
else |
301 |
{ |
302 |
bool flag3 = flag; |
303 |
if (!flag3) |
304 |
{ |
305 |
foreach (TransformationNode item in outputNode.Transforms) |
306 |
{ |
307 |
rect3 = new Int32Rect(0, 0, 0, 0); |
308 |
source2 = this.CreateInputFrame(this.inputs[item.Input], rect3); |
309 |
if (!(IsFormatOpaque(source2.Format) && (source2.DpiX == source2.DpiY))) |
310 |
{ |
311 |
flag3 = true; |
312 |
break; |
313 |
} |
314 |
} |
315 |
} |
316 |
if (flag3) |
317 |
{ |
318 |
visual = new DrawingVisual(); |
319 |
using (context = visual.RenderOpen()) |
320 |
{ |
321 |
if (flag) |
322 |
{ |
323 |
context.DrawRectangle(new SolidColorBrush(outputNode.BackgroundColor), null, rectangle); |
324 |
} |
325 |
foreach (TransformationNode item in outputNode.Transforms) |
326 |
{ |
327 |
outputRect = item.OutputRect; |
328 |
inputRect = item.InputRect; |
329 |
node2 = this.inputs[item.Input]; |
330 |
source2 = this.CreateInputFrame(node2, inputRect); |
331 |
if (!this.KeepImagesInCache) |
332 |
{ |
333 |
this.CloseInput(node.Input); |
334 |
} |
335 |
if (this.CopyMetadata) |
336 |
{ |
337 |
metadata = (BitmapMetadata)source2.Metadata; |
338 |
if (metadata != null) |
339 |
{ |
340 |
CopyBitmapMetadata(metadata, destinationMD); |
341 |
} |
342 |
} |
343 |
inputRect.Y -= node2.FirstScanLineLoaded; |
344 |
if (source2.DpiX == source2.DpiY) |
345 |
{ |
346 |
source4 = ScaleAndCropBitmap(ref outputRect, ref inputRect, source2); |
347 |
context.DrawImage(source4, outputRect); |
348 |
} |
349 |
else |
350 |
{ |
351 |
Rect r = Int32RectToRect(inputRect); |
352 |
if (source2.DpiX > source2.DpiY) |
353 |
{ |
354 |
r.Scale(1.0, source2.DpiX / source2.DpiY); |
355 |
} |
356 |
else |
357 |
{ |
358 |
r.Scale(source2.DpiY / source2.DpiX, 1.0); |
359 |
} |
360 |
Int32Rect sourceRect = RectToInt32Rect(r); |
361 |
CroppedBitmap imageSource = new CroppedBitmap(source2, sourceRect); |
362 |
context.DrawImage(imageSource, outputRect); |
363 |
} |
364 |
} |
365 |
context.Close(); |
366 |
} |
367 |
bitmap2 = new RenderTargetBitmap(outputNode.Width, outputNode.Height, 96.0, 96.0, PixelFormats.Default); |
368 |
bitmap2.Render(visual); |
369 |
bitmap3 = new FormatConvertedBitmap(bitmap2, format, null, 0.0); |
370 |
source = bitmap3; |
371 |
} |
372 |
else |
373 |
{ |
374 |
bitmap4 = new WriteableBitmap(outputNode.Width, outputNode.Height, 96.0, 96.0, format, null); |
375 |
foreach (TransformationNode item in outputNode.Transforms) |
376 |
{ |
377 |
outputRect = item.OutputRect; |
378 |
inputRect = item.InputRect; |
379 |
node2 = this.inputs[item.Input]; |
380 |
source2 = this.CreateInputFrame(node2, inputRect); |
381 |
if (!this.KeepImagesInCache) |
382 |
{ |
383 |
this.CloseInput(node.Input); |
384 |
} |
385 |
if (this.CopyMetadata) |
386 |
{ |
387 |
metadata = (BitmapMetadata)source2.Metadata; |
388 |
if (metadata != null) |
389 |
{ |
390 |
CopyBitmapMetadata(metadata, destinationMD); |
391 |
} |
392 |
} |
393 |
inputRect.Y -= node2.FirstScanLineLoaded; |
394 |
Int32Rect rect7 = RectToInt32Rect(outputRect); |
395 |
if ((rect7.Width > 0) && (rect7.Height > 0)) |
396 |
{ |
397 |
var _rec = Int32RectToRect(rect7); |
398 |
source4 = ScaleAndCropBitmap(ref _rec, ref inputRect, source2); |
399 |
if (source2.Format != format) |
400 |
{ |
401 |
source4 = new FormatConvertedBitmap(source4, format, null, 0.0); |
402 |
} |
403 |
int num2 = (source4.PixelWidth * source4.PixelHeight) * 4; |
404 |
if ((this.pixelStorage == null) || (this.pixelStorage.Length < num2)) |
405 |
{ |
406 |
this.pixelStorage = new byte[num2]; |
407 |
} |
408 |
int stride = ((rect7.Width * format.BitsPerPixel) + 7) / 8; |
409 |
source4.CopyPixels(this.pixelStorage, stride, 0); |
410 |
bitmap4.WritePixels(rect7, this.pixelStorage, stride, 0); |
411 |
} |
412 |
} |
413 |
source = bitmap4; |
414 |
} |
415 |
this.totalComposite++; |
416 |
} |
417 |
} |
418 |
List<ColorContext> list = new List<ColorContext> { |
419 |
new ColorContext(format) |
420 |
}; |
421 |
BitmapFrame frameToCache = BitmapFrame.Create(source, null, destinationMD, null); |
422 |
if (outputNode.CacheFrame) |
423 |
{ |
424 |
this.cachedFrames.Add(new CachedFrame(outputNode.FileName, frameToCache)); |
425 |
} |
426 |
encoder.Frames.Add(frameToCache); |
427 |
StreamEventArgs e = new StreamEventArgs(outputNode.FileName) |
428 |
{ |
429 |
MimeType = str, |
430 |
Compress = false, |
431 |
Progress = ((double)((this.totalBlank + this.totalComposite) + this.totalSingle)) / ((double)this.totalOutputs) |
432 |
}; |
433 |
EventHandler<StreamEventArgs> outputNeeded = this.OutputNeeded; |
434 |
if (outputNeeded == null) |
435 |
{ |
436 |
using (FileStream stream = new FileStream(outputNode.FileName, FileMode.Create)) |
437 |
{ |
438 |
encoder.Save(stream); |
439 |
stream.Flush(); |
440 |
} |
441 |
} |
442 |
else |
443 |
{ |
444 |
outputNeeded(this, e); |
445 |
encoder.Save(e.Stream); |
446 |
e.Stream.Flush(); |
447 |
} |
448 |
EventHandler<StreamEventArgs> outputCompleted = this.OutputCompleted; |
449 |
if (outputCompleted != null) |
450 |
{ |
451 |
outputCompleted(this, e); |
452 |
} |
453 |
outputNode.Transforms.Clear(); |
454 |
} |
455 |
|
456 |
private static void CopyBitmapMetadata(BitmapMetadata sourceMD, BitmapMetadata destinationMD) |
457 |
{ |
458 |
if (destinationMD.Format == sourceMD.Format) |
459 |
{ |
460 |
destinationMD.SetQuery("/", sourceMD); |
461 |
} |
462 |
else |
463 |
{ |
464 |
if (sourceMD.DateTaken != null) |
465 |
{ |
466 |
destinationMD.DateTaken = sourceMD.DateTaken; |
467 |
} |
468 |
if ((destinationMD.Format != "png") && (sourceMD.Format != "png")) |
469 |
{ |
470 |
if (sourceMD.ApplicationName != null) |
471 |
{ |
472 |
destinationMD.ApplicationName = sourceMD.ApplicationName; |
473 |
} |
474 |
if (sourceMD.Author != null) |
475 |
{ |
476 |
destinationMD.Author = sourceMD.Author; |
477 |
} |
478 |
if (sourceMD.CameraManufacturer != null) |
479 |
{ |
480 |
destinationMD.CameraManufacturer = sourceMD.CameraManufacturer; |
481 |
} |
482 |
if (sourceMD.CameraModel != null) |
483 |
{ |
484 |
destinationMD.CameraModel = sourceMD.CameraModel; |
485 |
} |
486 |
if (sourceMD.Comment != null) |
487 |
{ |
488 |
destinationMD.Comment = sourceMD.Comment; |
489 |
} |
490 |
if (sourceMD.Copyright != null) |
491 |
{ |
492 |
destinationMD.Copyright = sourceMD.Copyright; |
493 |
} |
494 |
if (sourceMD.Keywords != null) |
495 |
{ |
496 |
destinationMD.Keywords = sourceMD.Keywords; |
497 |
} |
498 |
destinationMD.Rating = sourceMD.Rating; |
499 |
if (sourceMD.Subject != null) |
500 |
{ |
501 |
destinationMD.Subject = sourceMD.Subject; |
502 |
} |
503 |
if (sourceMD.Title != null) |
504 |
{ |
505 |
destinationMD.Title = sourceMD.Title; |
506 |
} |
507 |
} |
508 |
} |
509 |
} |
510 |
|
511 |
internal BitmapDecoder CreateBitmapDecoder(StreamEventArgs streamEventArgs, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption) |
512 |
{ |
513 |
BitmapDecoder decoder = null; |
514 |
string absoluteUri; |
515 |
NotSupportedException exception; |
516 |
if (this.InputNeeded == null) |
517 |
{ |
518 |
Uri bitmapUri = new Uri(streamEventArgs.FileName, UriKind.RelativeOrAbsolute); |
519 |
absoluteUri = bitmapUri.AbsoluteUri; |
520 |
try |
521 |
{ |
522 |
decoder = BitmapDecoder.Create(bitmapUri, createOptions, cacheOption); |
523 |
} |
524 |
catch (NotSupportedException exception1) |
525 |
{ |
526 |
exception = exception1; |
527 |
throw new InvalidDataException("File is not in an image format '" + absoluteUri + "'. Exception message: " + exception.Message); |
528 |
} |
529 |
catch (OutOfMemoryException) |
530 |
{ |
531 |
decoder = BitmapDecoder.Create(bitmapUri, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None); |
532 |
} |
533 |
} |
534 |
else |
535 |
{ |
536 |
absoluteUri = streamEventArgs.FileName; |
537 |
this.InputNeeded(this, streamEventArgs); |
538 |
try |
539 |
{ |
540 |
decoder = BitmapDecoder.Create(streamEventArgs.Stream, createOptions, cacheOption); |
541 |
} |
542 |
catch (NotSupportedException exception3) |
543 |
{ |
544 |
exception = exception3; |
545 |
throw new InvalidDataException("File is not in an image format '" + absoluteUri + "'. Exception message: " + exception.Message); |
546 |
} |
547 |
} |
548 |
if (decoder.IsDownloading) |
549 |
{ |
550 |
bool downloadFailed = false; |
551 |
DispatcherFrame frame = new DispatcherFrame(); |
552 |
decoder.DownloadCompleted += delegate |
553 |
{ |
554 |
frame.Continue = false; |
555 |
}; |
556 |
decoder.DownloadFailed += delegate |
557 |
{ |
558 |
downloadFailed = true; |
559 |
frame.Continue = false; |
560 |
}; |
561 |
Dispatcher.PushFrame(frame); |
562 |
if (downloadFailed) |
563 |
{ |
564 |
throw new InvalidDataException("Download failed '" + absoluteUri + "'."); |
565 |
} |
566 |
} |
567 |
return decoder; |
568 |
} |
569 |
|
570 |
private BitmapSource CreateInputFrame(InputNode inputNode, Int32Rect rectNeeded) |
571 |
{ |
572 |
bool flag = false; |
573 |
if ((inputNode.InputSource == null) || (!rectNeeded.IsEmpty && ((rectNeeded.Y < inputNode.FirstScanLineLoaded) || ((rectNeeded.Height + rectNeeded.Y) > inputNode.LastScanLineLoaded)))) |
574 |
{ |
575 |
if (inputNode.InputSource != null) |
576 |
{ |
577 |
inputNode.InputSource = null; |
578 |
inputNode.FirstScanLineLoaded = 0; |
579 |
inputNode.LastScanLineLoaded = 0; |
580 |
GC.Collect(2); |
581 |
GC.Collect(2); |
582 |
} |
583 |
BitmapDecoder decoder = null; |
584 |
StreamEventArgs streamEventArgs = new StreamEventArgs(inputNode.FileName); |
585 |
decoder = this.CreateBitmapDecoder(streamEventArgs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad); |
586 |
if (!rectNeeded.IsEmpty) |
587 |
{ |
588 |
inputNode.FirstScanLineLoaded = 0; |
589 |
inputNode.LastScanLineLoaded = decoder.Frames[0].PixelHeight; |
590 |
} |
591 |
bool flag2 = false; |
592 |
if ((this.LargeFile != 0) && ((decoder.Frames[0].PixelWidth * rectNeeded.Height) <= this.LargeFile)) |
593 |
{ |
594 |
flag2 = true; |
595 |
} |
596 |
bool flag3 = false; |
597 |
if ((decoder.Frames[0].PixelHeight * decoder.Frames[0].PixelWidth) > this.LargeFile) |
598 |
{ |
599 |
flag3 = true; |
600 |
} |
601 |
if ((this.LargeFile != 0) && flag3) |
602 |
{ |
603 |
if ((!rectNeeded.IsEmpty && this.StripingEnabled) && flag2) |
604 |
{ |
605 |
flag = true; |
606 |
} |
607 |
decoder = this.CreateBitmapDecoder(streamEventArgs, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None); |
608 |
} |
609 |
BitmapSource frame = decoder.Frames[0]; |
610 |
StraightenFrame(ref frame); |
611 |
inputNode.PixelHeight = frame.PixelHeight; |
612 |
inputNode.PixelWidth = frame.PixelWidth; |
613 |
inputNode.Format = frame.Format; |
614 |
inputNode.Palette = frame.Palette; |
615 |
inputNode.InputSource = frame; |
616 |
if (flag) |
617 |
{ |
618 |
int num = (int)Math.Ceiling((double)((inputNode.PixelHeight * inputNode.PixelWidth) / ((double)this.LargeFile))); |
619 |
int pixelHeight = (int)Math.Round((double)(((double)inputNode.PixelHeight) / ((double)num))); |
620 |
int num3 = (int)Math.Truncate((double)(((double)rectNeeded.Y) / ((double)pixelHeight))); |
621 |
inputNode.FirstScanLineLoaded = pixelHeight * num3; |
622 |
inputNode.LastScanLineLoaded = Math.Min(inputNode.FirstScanLineLoaded + pixelHeight, inputNode.PixelHeight); |
623 |
if ((inputNode.FirstScanLineLoaded > rectNeeded.Y) || (inputNode.LastScanLineLoaded < (rectNeeded.Height + rectNeeded.Y))) |
624 |
{ |
625 |
inputNode.FirstScanLineLoaded = rectNeeded.Y; |
626 |
inputNode.LastScanLineLoaded = rectNeeded.Height + rectNeeded.Y; |
627 |
pixelHeight = inputNode.LastScanLineLoaded - inputNode.FirstScanLineLoaded; |
628 |
} |
629 |
Int32Rect sourceRect = new Int32Rect(0, inputNode.FirstScanLineLoaded, inputNode.PixelWidth, inputNode.LastScanLineLoaded - inputNode.FirstScanLineLoaded); |
630 |
if (((this.bitmap == null) || (this.bitmap.PixelHeight < pixelHeight)) || (this.bitmap.PixelWidth < inputNode.PixelWidth)) |
631 |
{ |
632 |
if (this.bitmap != null) |
633 |
{ |
634 |
this.bitmap = null; |
635 |
GC.Collect(); |
636 |
GC.Collect(); |
637 |
} |
638 |
this.bitmap = new WriteableBitmap(inputNode.PixelWidth, pixelHeight, 96.0, 96.0, inputNode.Format, inputNode.Palette); |
639 |
} |
640 |
int stride = ((inputNode.PixelWidth * inputNode.Format.BitsPerPixel) + 7) / 8; |
641 |
int num5 = stride * pixelHeight; |
642 |
byte[] pixels = new byte[num5]; |
643 |
frame.CopyPixels(sourceRect, pixels, stride, 0); |
644 |
frame = null; |
645 |
sourceRect.Y = 0; |
646 |
this.bitmap.WritePixels(sourceRect, pixels, stride, 0); |
647 |
inputNode.InputSource = this.bitmap; |
648 |
inputNode.Format = this.bitmap.Format; |
649 |
inputNode.Palette = this.bitmap.Palette; |
650 |
GC.Collect(2); |
651 |
GC.Collect(2); |
652 |
} |
653 |
if (this.InputCompleted != null) |
654 |
{ |
655 |
this.InputCompleted(this, streamEventArgs); |
656 |
} |
657 |
} |
658 |
return inputNode.InputSource; |
659 |
} |
660 |
|
661 |
[FileIOPermission(SecurityAction.LinkDemand)] |
662 |
public void Execute() |
663 |
{ |
664 |
Debug.WriteLine("Inputs:" + this.inputs.Count); |
665 |
Debug.WriteLine("Outputs:" + this.outputs.Count); |
666 |
int num = 0; |
667 |
foreach (OutputNode node in this.outputs) |
668 |
{ |
669 |
this.CloseOutputImpl(node); |
670 |
num++; |
671 |
if ((num % 30) == 0) |
672 |
{ |
673 |
GC.Collect(2); |
674 |
} |
675 |
} |
676 |
this.outputs.Clear(); |
677 |
Debug.WriteLine("Single Outputs written:" + this.totalSingle); |
678 |
Debug.WriteLine("Composite Outputs written:" + this.totalComposite); |
679 |
Debug.WriteLine("Blank Outputs written:" + this.totalBlank); |
680 |
} |
681 |
|
682 |
public Point GetDpi(string fileName) |
683 |
{ |
684 |
Int32Rect rectNeeded = new Int32Rect(0, 0, 0, 0); |
685 |
BitmapSource source = this.CreateInputFrame(this.inputs[fileName], rectNeeded); |
686 |
return new Point(source.DpiX, source.DpiY); |
687 |
} |
688 |
|
689 |
private BitmapEncoder GetEncoder(OutputNode outputNode, out PixelFormat pixelFormat, out string mimeType) |
690 |
{ |
691 |
pixelFormat = this.ChoosePixelFormat(outputNode.Format); |
692 |
switch (outputNode.Format) |
693 |
{ |
694 |
case ImageFormat.Jpg: |
695 |
{ |
696 |
JpegBitmapEncoder encoder = new JpegBitmapEncoder |
697 |
{ |
698 |
QualityLevel = 1 + ((int)Math.Round((double)(outputNode.Quality * 99.0))) |
699 |
}; |
700 |
mimeType = "image/jpeg"; |
701 |
return encoder; |
702 |
} |
703 |
case ImageFormat.Png: |
704 |
mimeType = "image/png"; |
705 |
return new PngBitmapEncoder(); |
706 |
|
707 |
case ImageFormat.Wdp: |
708 |
{ |
709 |
WmpBitmapEncoder encoder2 = new WmpBitmapEncoder |
710 |
{ |
711 |
QualityLevel = (byte)Math.Round((double)(105.0 - (outputNode.Quality * 100.0))) |
712 |
}; |
713 |
if (outputNode.Quality == 1.0) |
714 |
{ |
715 |
encoder2.QualityLevel = 1; |
716 |
} |
717 |
encoder2.OverlapLevel = 0; |
718 |
encoder2.InterleavedAlpha = true; |
719 |
encoder2.UseCodecOptions = true; |
720 |
mimeType = "image/vnd.ms-photo"; |
721 |
return encoder2; |
722 |
} |
723 |
} |
724 |
pixelFormat = PixelFormats.Default; |
725 |
mimeType = ""; |
726 |
return null; |
727 |
} |
728 |
|
729 |
public PixelFormat GetPixelFormat(string fileName) |
730 |
{ |
731 |
Int32Rect rectNeeded = new Int32Rect(0, 0, 0, 0); |
732 |
return this.CreateInputFrame(this.inputs[fileName], rectNeeded).Format; |
733 |
} |
734 |
|
735 |
public Point GetPixelSize(string fileName) |
736 |
{ |
737 |
Int32Rect rectNeeded = new Int32Rect(0, 0, 0, 0); |
738 |
InputNode inputNode = this.inputs[fileName]; |
739 |
this.CreateInputFrame(inputNode, rectNeeded); |
740 |
return new Point((double)inputNode.PixelWidth, (double)inputNode.PixelHeight); |
741 |
} |
742 |
|
743 |
public static Rect Int32RectToRect(Int32Rect r) |
744 |
{ |
745 |
if (r.IsEmpty) |
746 |
{ |
747 |
return Rect.Empty; |
748 |
} |
749 |
return new Rect((double)r.X, (double)r.Y, (double)r.Width, (double)r.Height); |
750 |
} |
751 |
|
752 |
private static bool IsFormatOpaque(PixelFormat pixelFormat) |
753 |
{ |
754 |
return ((((((pixelFormat == PixelFormats.Bgr101010) || (pixelFormat == PixelFormats.Bgr24)) || ((pixelFormat == PixelFormats.Bgr32) || (pixelFormat == PixelFormats.Bgr555))) || (((pixelFormat == PixelFormats.Bgr565) || (pixelFormat == PixelFormats.BlackWhite)) || ((pixelFormat == PixelFormats.Cmyk32) || (pixelFormat == PixelFormats.Gray16)))) || ((((pixelFormat == PixelFormats.Gray2) || (pixelFormat == PixelFormats.Gray32Float)) || ((pixelFormat == PixelFormats.Gray4) || (pixelFormat == PixelFormats.Gray8))) || ((pixelFormat == PixelFormats.Rgb128Float) || (pixelFormat == PixelFormats.Rgb24)))) || (pixelFormat == PixelFormats.Rgb48)); |
755 |
} |
756 |
|
757 |
public bool IsOpaque(string fileName) |
758 |
{ |
759 |
return IsFormatOpaque(this.GetPixelFormat(fileName)); |
760 |
} |
761 |
|
762 |
private static bool RectEqualWithTolerance(Rect a, Rect b) |
763 |
{ |
764 |
return ((a == b) || ((((Math.Abs((double)(a.X - b.X)) < 1E-10) && (Math.Abs((double)(a.Y - b.Y)) < 1E-10)) && (Math.Abs((double)(a.Width - b.Width)) < 1E-10)) && (Math.Abs((double)(a.Height - b.Height)) < 1E-10))); |
765 |
} |
766 |
|
767 |
public static Int32Rect RectToInt32Rect(Rect r) |
768 |
{ |
769 |
int x = (int)Math.Round(r.X); |
770 |
int y = (int)Math.Round(r.Y); |
771 |
int width = ((int)Math.Round(r.Right)) - x; |
772 |
return new Int32Rect(x, y, width, ((int)Math.Round(r.Bottom)) - y); |
773 |
} |
774 |
|
775 |
private static BitmapSource ScaleAndCropBitmap(ref Rect outputRect, ref Int32Rect inputRect, BitmapSource inputSource) |
776 |
{ |
777 |
bool flag = (Math.Round(outputRect.Width) != inputRect.Width) || (Math.Round(outputRect.Height) != inputRect.Height); |
778 |
bool flag2 = (((inputRect.X != 0) || (inputRect.Y != 0)) || (inputRect.Width != inputSource.PixelWidth)) || (inputRect.Height != inputSource.PixelHeight); |
779 |
BitmapSource source = inputSource; |
780 |
if (flag2) |
781 |
{ |
782 |
source = new CroppedBitmap(source, inputRect); |
783 |
} |
784 |
if (flag) |
785 |
{ |
786 |
ScaleTransform newTransform = new ScaleTransform(outputRect.Width / ((double)inputRect.Width), outputRect.Height / ((double)inputRect.Height)); |
787 |
source = new TransformedBitmap(source, newTransform); |
788 |
} |
789 |
return source; |
790 |
} |
791 |
|
792 |
internal static bool StraightenFrame(ref BitmapSource frame) |
793 |
{ |
794 |
bool flag = false; |
795 |
BitmapMetadata metadata = (BitmapMetadata)frame.Metadata; |
796 |
if ((metadata != null) && metadata.ContainsQuery("/app1/ifd/exif:{uint=274}")) |
797 |
{ |
798 |
int angle = 0; |
799 |
object query = metadata.GetQuery("/app1/ifd/exif:{uint=274}"); |
800 |
if (query.GetType() == typeof(ushort)) |
801 |
{ |
802 |
switch (((ushort)query)) |
803 |
{ |
804 |
case 6: |
805 |
angle = 90; |
806 |
break; |
807 |
|
808 |
case 8: |
809 |
angle = 270; |
810 |
break; |
811 |
|
812 |
case 3: |
813 |
angle = 180; |
814 |
break; |
815 |
} |
816 |
} |
817 |
if (angle != 0) |
818 |
{ |
819 |
RotateTransform newTransform = new RotateTransform(angle, ((double)frame.PixelWidth) / 2.0, ((double)frame.PixelHeight) / 2.0); |
820 |
BitmapSource source = new TransformedBitmap(frame, newTransform); |
821 |
BitmapMetadata metadata2 = metadata.Clone(); |
822 |
metadata2.SetQuery("/app1/ifd/exif:{uint=274}", (ushort)1); |
823 |
switch (angle) |
824 |
{ |
825 |
case 90: |
826 |
case 270: |
827 |
{ |
828 |
flag = true; |
829 |
object obj3 = null; |
830 |
object obj4 = null; |
831 |
if (metadata2.ContainsQuery("/app1/ifd/exif:{uint=282}")) |
832 |
{ |
833 |
obj3 = metadata2.GetQuery("/app1/ifd/exif:{uint=282}"); |
834 |
} |
835 |
if (metadata2.ContainsQuery("/app1/ifd/exif:{uint=283}")) |
836 |
{ |
837 |
obj4 = metadata2.GetQuery("/app1/ifd/exif:{uint=283}"); |
838 |
} |
839 |
if (obj3 != null) |
840 |
{ |
841 |
metadata2.SetQuery("/app1/ifd/exif:{uint=283}", obj3); |
842 |
} |
843 |
else |
844 |
{ |
845 |
metadata2.RemoveQuery("/app1/ifd/exif:{uint=283}"); |
846 |
} |
847 |
if (obj4 != null) |
848 |
{ |
849 |
metadata2.SetQuery("/app1/ifd/exif:{uint=282}", obj4); |
850 |
} |
851 |
else |
852 |
{ |
853 |
metadata2.RemoveQuery("/app1/ifd/exif:{uint=282}"); |
854 |
} |
855 |
object obj5 = null; |
856 |
object obj6 = null; |
857 |
if (metadata2.ContainsQuery("/app1/ifd/exif/subifd:{uint=40962}")) |
858 |
{ |
859 |
obj5 = metadata2.GetQuery("/app1/ifd/exif/subifd:{uint=40962}"); |
860 |
} |
861 |
if (metadata2.ContainsQuery("/app1/ifd/exif/subifd:{uint=40963}")) |
862 |
{ |
863 |
obj6 = metadata2.GetQuery("/app1/ifd/exif/subifd:{uint=40963}"); |
864 |
} |
865 |
if (obj5 != null) |
866 |
{ |
867 |
metadata2.SetQuery("/app1/ifd/exif/subifd:{uint=40963}", obj5); |
868 |
} |
869 |
else |
870 |
{ |
871 |
metadata2.RemoveQuery("/app1/ifd/exif/subifd:{uint=40963}"); |
872 |
} |
873 |
if (obj6 != null) |
874 |
{ |
875 |
metadata2.SetQuery("/app1/ifd/exif/subifd:{uint=40962}", obj6); |
876 |
} |
877 |
else |
878 |
{ |
879 |
metadata2.RemoveQuery("/app1/ifd/exif/subifd:{uint=40962}"); |
880 |
} |
881 |
break; |
882 |
} |
883 |
} |
884 |
frame = source; |
885 |
} |
886 |
} |
887 |
return flag; |
888 |
} |
889 |
|
890 |
// Properties |
891 |
public bool CopyMetadata { get; set; } |
892 |
|
893 |
public bool KeepImagesInCache { get; set; } |
894 |
|
895 |
public int LargeFile |
896 |
{ |
897 |
get |
898 |
{ |
899 |
return this.largeFile; |
900 |
} |
901 |
set |
902 |
{ |
903 |
this.largeFile = value; |
904 |
} |
905 |
} |
906 |
|
907 |
public bool OutputAlpha { get; set; } |
908 |
|
909 |
public bool StripingEnabled { get; set; } |
910 |
|
911 |
// Nested Types |
912 |
private class CachedFrameCollection : KeyedCollection<string, CachedFrame> |
913 |
{ |
914 |
// Methods |
915 |
protected override string GetKeyForItem(CachedFrame item) |
916 |
{ |
917 |
return item.path; |
918 |
} |
919 |
} |
920 |
|
921 |
private class InputNode |
922 |
{ |
923 |
// Fields |
924 |
private string fileName; |
925 |
private int firstScanLineLoaded; |
926 |
private int lastScanLineLoaded; |
927 |
|
928 |
// Methods |
929 |
public InputNode(string fileName) |
930 |
{ |
931 |
this.fileName = fileName; |
932 |
this.PixelHeight = 0; |
933 |
this.PixelWidth = 0; |
934 |
this.firstScanLineLoaded = 0; |
935 |
this.lastScanLineLoaded = 0; |
936 |
this.Format = PixelFormats.Default; |
937 |
this.Palette = null; |
938 |
} |
939 |
|
940 |
// Properties |
941 |
public string FileName |
942 |
{ |
943 |
get |
944 |
{ |
945 |
return this.fileName; |
946 |
} |
947 |
} |
948 |
|
949 |
public int FirstScanLineLoaded |
950 |
{ |
951 |
get |
952 |
{ |
953 |
return this.firstScanLineLoaded; |
954 |
} |
955 |
set |
956 |
{ |
957 |
this.firstScanLineLoaded = value; |
958 |
} |
959 |
} |
960 |
|
961 |
public PixelFormat Format { get; set; } |
962 |
|
963 |
public BitmapSource InputSource { get; set; } |
964 |
|
965 |
public int LastScanLineLoaded |
966 |
{ |
967 |
get |
968 |
{ |
969 |
return this.lastScanLineLoaded; |
970 |
} |
971 |
set |
972 |
{ |
973 |
this.lastScanLineLoaded = value; |
974 |
} |
975 |
} |
976 |
|
977 |
public BitmapPalette Palette { get; set; } |
978 |
|
979 |
public int PixelHeight { get; set; } |
980 |
|
981 |
public int PixelWidth { get; set; } |
982 |
} |
983 |
|
984 |
private class InputNodeCollection : KeyedCollection<string, BitmapTransformer.InputNode> |
985 |
{ |
986 |
// Methods |
987 |
protected override string GetKeyForItem(BitmapTransformer.InputNode item) |
988 |
{ |
989 |
return item.FileName; |
990 |
} |
991 |
} |
992 |
|
993 |
private class OutputNode |
994 |
{ |
995 |
// Fields |
996 |
private Color backgroundColor; |
997 |
public bool CacheFrame; |
998 |
private string fileName; |
999 |
private ImageFormat format; |
1000 |
private int height; |
1001 |
private double quality; |
1002 |
private List<BitmapTransformer.TransformationNode> transforms; |
1003 |
private int width; |
1004 |
|
1005 |
// Methods |
1006 |
public OutputNode(string fileName) |
1007 |
{ |
1008 |
this.fileName = fileName; |
1009 |
this.transforms = new List<BitmapTransformer.TransformationNode>(); |
1010 |
this.CacheFrame = false; |
1011 |
} |
1012 |
|
1013 |
// Properties |
1014 |
public Color BackgroundColor |
1015 |
{ |
1016 |
get |
1017 |
{ |
1018 |
return this.backgroundColor; |
1019 |
} |
1020 |
set |
1021 |
{ |
1022 |
this.backgroundColor = value; |
1023 |
} |
1024 |
} |
1025 |
|
1026 |
public string FileName |
1027 |
{ |
1028 |
get |
1029 |
{ |
1030 |
return this.fileName; |
1031 |
} |
1032 |
} |
1033 |
|
1034 |
public ImageFormat Format |
1035 |
{ |
1036 |
get |
1037 |
{ |
1038 |
return this.format; |
1039 |
} |
1040 |
set |
1041 |
{ |
1042 |
this.format = value; |
1043 |
} |
1044 |
} |
1045 |
|
1046 |
public int Height |
1047 |
{ |
1048 |
get |
1049 |
{ |
1050 |
return this.height; |
1051 |
} |
1052 |
set |
1053 |
{ |
1054 |
Debug.Assert(value > 0, "Height must be greater than 0."); |
1055 |
this.height = value; |
1056 |
} |
1057 |
} |
1058 |
|
1059 |
public double Quality |
1060 |
{ |
1061 |
get |
1062 |
{ |
1063 |
return this.quality; |
1064 |
} |
1065 |
set |
1066 |
{ |
1067 |
Debug.Assert((value >= 0.0) && (value <= 1.0), "Quality must be between 0 and 1."); |
1068 |
this.quality = value; |
1069 |
} |
1070 |
} |
1071 |
|
1072 |
public List<BitmapTransformer.TransformationNode> Transforms |
1073 |
{ |
1074 |
get |
1075 |
{ |
1076 |
return this.transforms; |
1077 |
} |
1078 |
} |
1079 |
|
1080 |
public int Width |
1081 |
{ |
1082 |
get |
1083 |
{ |
1084 |
return this.width; |
1085 |
} |
1086 |
set |
1087 |
{ |
1088 |
Debug.Assert(value > 0, "Width must be greater than 0."); |
1089 |
this.width = value; |
1090 |
} |
1091 |
} |
1092 |
} |
1093 |
|
1094 |
private class OutputNodeCollection : KeyedCollection<string, BitmapTransformer.OutputNode> |
1095 |
{ |
1096 |
// Methods |
1097 |
protected override string GetKeyForItem(BitmapTransformer.OutputNode item) |
1098 |
{ |
1099 |
return item.FileName; |
1100 |
} |
1101 |
} |
1102 |
|
1103 |
private class TransformationNode |
1104 |
{ |
1105 |
// Fields |
1106 |
private string input; |
1107 |
private Int32Rect inputRect; |
1108 |
private Rect outputRect; |
1109 |
|
1110 |
// Properties |
1111 |
public string Input |
1112 |
{ |
1113 |
get |
1114 |
{ |
1115 |
return this.input; |
1116 |
} |
1117 |
set |
1118 |
{ |
1119 |
this.input = value; |
1120 |
} |
1121 |
} |
1122 |
|
1123 |
public Int32Rect InputRect |
1124 |
{ |
1125 |
get |
1126 |
{ |
1127 |
return this.inputRect; |
1128 |
} |
1129 |
set |
1130 |
{ |
1131 |
this.inputRect = value; |
1132 |
} |
1133 |
} |
1134 |
|
1135 |
public Rect OutputRect |
1136 |
{ |
1137 |
get |
1138 |
{ |
1139 |
return this.outputRect; |
1140 |
} |
1141 |
set |
1142 |
{ |
1143 |
this.outputRect = value; |
1144 |
} |
1145 |
} |
1146 |
} |
1147 |
} |
1148 |
|
1149 |
|
1150 |
|
1151 |
} |