VectSharp  2.2.1
A light library for C# vector graphics
ImageFormats.cs
1 /*
2  VectSharp - A light library for C# vector graphics.
3  Copyright (C) 2020-2022 Giorgio Bianchini
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, version 3.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public License
15  along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 using System;
19 using System.IO;
20 using System.IO.Compression;
21 using System.Runtime.InteropServices;
22 using System.Threading;
23 
24 namespace VectSharp
25 {
26  internal static class PNGEncoder
27  {
28  public unsafe static void SavePNG(byte* image, int width, int height, bool hasAlpha, Stream fs, FilterModes filter, int threadCount = 0)
29  {
30  if (threadCount == 0)
31  {
32  threadCount = filter == FilterModes.Adaptive ? Math.Max(1, Math.Min(width / 600, Environment.ProcessorCount - 2)) : 1;
33  }
34 
35  //Header
36  fs.Write(new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, 0, 8);
37 
38  //IHDR chunk
39  fs.WriteInt(13);
40  using (MemoryStream ihdr = new MemoryStream(13))
41  {
42  ihdr.WriteASCIIString("IHDR");
43  ihdr.WriteInt(width);
44  ihdr.WriteInt(height);
45  ihdr.WriteByte(8); //Bit depth
46 
47  if (hasAlpha)
48  {
49  ihdr.WriteByte(6); //Colour type
50  }
51  else
52  {
53  ihdr.WriteByte(2); //Colour type
54  }
55 
56  ihdr.WriteByte(0); //Compression method
57  ihdr.WriteByte(0); //Filter method
58  ihdr.WriteByte(0); //Interlace
59 
60  ihdr.Seek(0, SeekOrigin.Begin);
61  ihdr.CopyTo(fs);
62 
63  fs.WriteUInt(CRC32.ComputeCRC(ihdr));
64  }
65 
66  //IDAT chunk
67  IntPtr filteredImage;
68 
69  if (threadCount > 1)
70  {
71  filteredImage = FilterImageData(image, width, height, hasAlpha ? 4 : 3, FilterModes.Adaptive, threadCount);
72  }
73  else
74  {
75  filteredImage = FilterImageData(image, width, height, hasAlpha ? 4 : 3, FilterModes.Adaptive);
76  }
77 
78  using (MemoryStream compressedImage = StreamUtils.ZLibCompress(filteredImage, height * (width * (hasAlpha ? 4 : 3) + 1)))
79  {
80  compressedImage.Seek(0, SeekOrigin.Begin);
81  fs.WriteUInt((uint)compressedImage.Length);
82  fs.WriteASCIIString("IDAT");
83  compressedImage.Seek(0, SeekOrigin.Begin);
84  compressedImage.CopyTo(fs);
85 
86  fs.WriteUInt(CRC32.ComputeCRC(compressedImage.GetBuffer(), (int)compressedImage.Length, new byte[] { 73, 68, 65, 84 }));
87  }
88 
89  Marshal.FreeHGlobal(filteredImage);
90 
91  //IEND chunk
92  fs.WriteInt(0);
93  fs.WriteASCIIString("IEND");
94  fs.Write(new byte[] { 0xAE, 0x42, 0x60, 0x82 }, 0, 4);
95 
96  }
97 
98  internal enum FilterModes
99  {
100  None = 0,
101  Sub = 1,
102  Up = 2,
103  Average = 3,
104  Paeth = 4,
105  Adaptive = -1
106  }
107 
108  internal unsafe static void FilterRow(byte* image, int width, int pixelSize, int stride, int y, FilterModes filter, byte* destinationImage, byte* tempBuffer)
109  {
110  int startIndex = y * stride;
111  int destinationIndex = y * (stride + 1);
112 
113  if (filter == FilterModes.None)
114  {
115  destinationImage[destinationIndex++] = 0;
116  for (int x = 0; x < width; x++)
117  {
118  destinationImage[destinationIndex++] = image[startIndex++];
119  destinationImage[destinationIndex++] = image[startIndex++];
120  destinationImage[destinationIndex++] = image[startIndex++];
121 
122  if (pixelSize == 4)
123  {
124  destinationImage[destinationIndex++] = image[startIndex++];
125  }
126 
127  }
128  }
129  else if (filter == FilterModes.Sub)
130  {
131  destinationImage[destinationIndex++] = 1;
132  for (int x = 0; x < width; x++)
133  {
134  if (x > 0)
135  {
136  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
137  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
138  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
139  if (pixelSize == 4)
140  {
141  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
142  }
143  }
144  else
145  {
146  destinationImage[destinationIndex++] = image[startIndex++];
147  destinationImage[destinationIndex++] = image[startIndex++];
148  destinationImage[destinationIndex++] = image[startIndex++];
149  if (pixelSize == 4)
150  {
151  destinationImage[destinationIndex++] = image[startIndex++];
152  }
153  }
154  }
155  }
156  else if (filter == FilterModes.Up)
157  {
158  destinationImage[destinationIndex++] = 2;
159  for (int x = 0; x < width; x++)
160  {
161  if (y > 0)
162  {
163  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
164  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
165  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
166  if (pixelSize == 4)
167  {
168  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
169  }
170  }
171  else
172  {
173  destinationImage[destinationIndex++] = image[startIndex++];
174  destinationImage[destinationIndex++] = image[startIndex++];
175  destinationImage[destinationIndex++] = image[startIndex++];
176  if (pixelSize == 4)
177  {
178  destinationImage[destinationIndex++] = image[startIndex++];
179  }
180  }
181  }
182  }
183  else if (filter == FilterModes.Average)
184  {
185  destinationImage[destinationIndex++] = 3;
186  for (int x = 0; x < width; x++)
187  {
188  if (x > 0 && y > 0)
189  {
190  destinationImage[destinationIndex++] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex++ - stride]) / 2);
191  destinationImage[destinationIndex++] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex++ - stride]) / 2);
192  destinationImage[destinationIndex++] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex++ - stride]) / 2);
193  if (pixelSize == 4)
194  {
195  destinationImage[destinationIndex++] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex++ - stride]) / 2);
196  }
197  }
198  else if (x > 0 && y == 0)
199  {
200  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize] / 2);
201  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize] / 2);
202  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize] / 2);
203  if (pixelSize == 4)
204  {
205  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize] / 2);
206  }
207  }
208  else if (x == 0 && y > 0)
209  {
210  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride] / 2);
211  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride] / 2);
212  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride] / 2);
213  if (pixelSize == 4)
214  {
215  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride] / 2);
216  }
217  }
218  else
219  {
220  destinationImage[destinationIndex++] = image[startIndex++];
221  destinationImage[destinationIndex++] = image[startIndex++];
222  destinationImage[destinationIndex++] = image[startIndex++];
223  if (pixelSize == 4)
224  {
225  destinationImage[destinationIndex++] = image[startIndex++];
226  }
227  }
228  }
229  }
230  else if (filter == FilterModes.Paeth)
231  {
232  destinationImage[destinationIndex++] = 4;
233  for (int x = 0; x < width; x++)
234  {
235  if (x > 0 && y > 0)
236  {
237  destinationImage[destinationIndex++] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex++ - pixelSize - stride]));
238  destinationImage[destinationIndex++] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex++ - pixelSize - stride]));
239  destinationImage[destinationIndex++] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex++ - pixelSize - stride]));
240  if (pixelSize == 4)
241  {
242  destinationImage[destinationIndex++] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex++ - pixelSize - stride]));
243  }
244  }
245  else if (x > 0 && y == 0)
246  {
247  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
248  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
249  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
250  if (pixelSize == 4)
251  {
252  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - pixelSize]);
253  }
254  }
255  else if (x == 0 && y > 0)
256  {
257  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
258  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
259  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
260  if (pixelSize == 4)
261  {
262  destinationImage[destinationIndex++] = (byte)(image[startIndex] - image[startIndex++ - stride]);
263  }
264  }
265  else
266  {
267  destinationImage[destinationIndex++] = image[startIndex++];
268  destinationImage[destinationIndex++] = image[startIndex++];
269  destinationImage[destinationIndex++] = image[startIndex++];
270  if (pixelSize == 4)
271  {
272  destinationImage[destinationIndex++] = image[startIndex++];
273  }
274  }
275  }
276  }
277  else if (filter == FilterModes.Adaptive)
278  {
279  int tempIndex = 0;
280 
281  int none = 0;
282  int sub = 0;
283  int up = 0;
284  int average = 0;
285  int paeth = 0;
286 
287  for (int x = 0; x < width; x++)
288  {
289  for (int i = 0; i < pixelSize; i++)
290  {
291  //none
292  tempBuffer[tempIndex] = image[startIndex];
293 
294  //sub
295  if (x > 0)
296  {
297  tempBuffer[tempIndex + stride] = (byte)(image[startIndex] - image[startIndex - pixelSize]);
298  }
299  else
300  {
301  tempBuffer[tempIndex + stride] = image[startIndex];
302  }
303 
304  //up
305  if (y > 0)
306  {
307  tempBuffer[tempIndex + stride * 2] = (byte)(image[startIndex] - image[startIndex - stride]);
308  }
309  else
310  {
311  tempBuffer[tempIndex + stride * 2] = image[startIndex];
312  }
313 
314  //average
315  if (x > 0 && y > 0)
316  {
317  tempBuffer[tempIndex + stride * 3] = (byte)(image[startIndex] - (image[startIndex - pixelSize] + image[startIndex - stride]) / 2);
318  }
319  else if (x > 0 && y == 0)
320  {
321  tempBuffer[tempIndex + stride * 3] = (byte)(image[startIndex] - image[startIndex - pixelSize] / 2);
322  }
323  else if (x == 0 && y > 0)
324  {
325  tempBuffer[tempIndex + stride * 3] = (byte)(image[startIndex] - image[startIndex - stride] / 2);
326  }
327  else
328  {
329  tempBuffer[tempIndex + stride * 3] = image[startIndex];
330  }
331 
332  //paeth
333  if (x > 0 && y > 0)
334  {
335  tempBuffer[tempIndex + stride * 4] = (byte)(image[startIndex] - PaethPredictor(image[startIndex - pixelSize], image[startIndex - stride], image[startIndex - pixelSize - stride]));
336  }
337  else if (x > 0 && y == 0)
338  {
339  tempBuffer[tempIndex + stride * 4] = (byte)(image[startIndex] - image[startIndex - pixelSize]);
340  }
341  else if (x == 0 && y > 0)
342  {
343  tempBuffer[tempIndex + stride * 4] = (byte)(image[startIndex] - image[startIndex - stride]);
344  }
345  else
346  {
347  tempBuffer[tempIndex + stride * 4] = image[startIndex];
348  }
349 
350  none += tempBuffer[tempIndex];
351  sub += tempBuffer[tempIndex + stride];
352  up += tempBuffer[tempIndex + stride * 2];
353  average += tempBuffer[tempIndex + stride * 3];
354  paeth += tempBuffer[tempIndex + stride * 4];
355  tempIndex++;
356  startIndex++;
357  }
358  }
359 
360  if (paeth <= average && paeth <= up && paeth <= sub && paeth <= none)
361  {
362  destinationImage[destinationIndex++] = 4;
363  tempIndex = stride * 4;
364  for (int i = 0; i < stride; i++)
365  {
366  destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
367  }
368  }
369  else if (average <= up && average <= sub && average <= none)
370  {
371  destinationImage[destinationIndex++] = 3;
372  tempIndex = stride * 3;
373  for (int i = 0; i < stride; i++)
374  {
375  destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
376  }
377  }
378  else if (up <= sub && up <= none)
379  {
380  destinationImage[destinationIndex++] = 2;
381  tempIndex = stride * 2;
382  for (int i = 0; i < stride; i++)
383  {
384  destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
385  }
386  }
387  else if (sub <= none)
388  {
389  destinationImage[destinationIndex++] = 1;
390  tempIndex = stride;
391  for (int i = 0; i < stride; i++)
392  {
393  destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
394  }
395  }
396  else
397  {
398  destinationImage[destinationIndex++] = 0;
399  tempIndex = 0;
400  for (int i = 0; i < stride; i++)
401  {
402  destinationImage[destinationIndex++] = tempBuffer[tempIndex++];
403  }
404  }
405  }
406  }
407 
408  internal unsafe static IntPtr FilterImageData(byte* image, int width, int height, int pixelSize, FilterModes filter)
409  {
410  IntPtr filteredMemory = Marshal.AllocHGlobal(height * (1 + width * pixelSize));
411 
412  IntPtr tempBuffer;
413 
414  if (filter == FilterModes.Adaptive)
415  {
416  tempBuffer = Marshal.AllocHGlobal(5 * width * pixelSize);
417  }
418  else
419  {
420  tempBuffer = IntPtr.Zero;
421  }
422 
423  int stride = width * pixelSize;
424 
425  byte* filteredPointer = (byte*)filteredMemory;
426 
427  byte* tempPointer = (byte*)tempBuffer;
428 
429  for (int y = 0; y < height; y++)
430  {
431  FilterRow(image, width, pixelSize, stride, y, filter, filteredPointer, tempPointer);
432  }
433 
434  Marshal.FreeHGlobal(tempBuffer);
435 
436  return filteredMemory;
437  }
438 
439  internal unsafe static IntPtr FilterImageData(byte* image, int width, int height, int pixelSize, FilterModes filter, int threadCount)
440  {
441  threadCount = Math.Min(threadCount, height);
442 
443  IntPtr filteredMemory = Marshal.AllocHGlobal(height * (1 + width * pixelSize));
444 
445  int stride = width * pixelSize;
446 
447  byte* filteredPointer = (byte*)filteredMemory;
448 
449  IntPtr[] tempBuffers = new IntPtr[threadCount];
450 
451  Thread[] threads = new Thread[threadCount];
452  int[] rowsByThread = new int[threadCount];
453  EventWaitHandle[] signalsFromThreads = new EventWaitHandle[threadCount];
454  EventWaitHandle[] signalsToThreads = new EventWaitHandle[threadCount];
455 
456  int firstNeededRow = threadCount;
457 
458  for (int i = 0; i < threadCount; i++)
459  {
460  rowsByThread[i] = i;
461  signalsFromThreads[i] = new EventWaitHandle(false, EventResetMode.ManualReset);
462  signalsToThreads[i] = new EventWaitHandle(false, EventResetMode.ManualReset);
463 
464  if (filter == FilterModes.Adaptive)
465  {
466  tempBuffers[i] = Marshal.AllocHGlobal(5 * width * pixelSize);
467  }
468  else
469  {
470  tempBuffers[i] = IntPtr.Zero;
471  }
472 
473  byte* tempPointer = (byte*)tempBuffers[i];
474 
475  int threadIndex = i;
476 
477  threads[i] = new Thread(() =>
478  {
479  while (rowsByThread[threadIndex] >= 0)
480  {
481  FilterRow(image, width, pixelSize, stride, rowsByThread[threadIndex], filter, filteredPointer, tempPointer);
482  EventWaitHandle.SignalAndWait(signalsFromThreads[threadIndex], signalsToThreads[threadIndex]);
483  signalsToThreads[threadIndex].Reset();
484  }
485  });
486  }
487 
488  int finished = 0;
489 
490  for (int i = 0; i < threadCount; i++)
491  {
492  threads[i].Start();
493  }
494 
495  while (finished < threadCount)
496  {
497  int threadInd = EventWaitHandle.WaitAny(signalsFromThreads);
498 
499  signalsFromThreads[threadInd].Reset();
500 
501  if (firstNeededRow < height)
502  {
503  rowsByThread[threadInd] = firstNeededRow;
504  firstNeededRow++;
505  signalsToThreads[threadInd].Set();
506  }
507  else
508  {
509  rowsByThread[threadInd] = -1;
510  signalsToThreads[threadInd].Set();
511  finished++;
512  }
513  }
514 
515  for (int i = 0; i < threadCount; i++)
516  {
517  if (filter == FilterModes.Adaptive)
518  {
519  Marshal.FreeHGlobal(tempBuffers[i]);
520  }
521  }
522 
523  return filteredMemory;
524  }
525 
526  //Based on http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html
527  internal static byte PaethPredictor(byte a, byte b, byte c)
528  {
529  int p = (int)a + (int)b - (int)c;
530  int pa = Math.Abs(p - (int)a);
531  int pb = Math.Abs(p - (int)b);
532  int pc = Math.Abs(p - (int)c);
533 
534  if (pa <= pb && pa <= pc)
535  {
536  return a;
537  }
538  else if (pb <= pc)
539  {
540  return b;
541  }
542  else
543  {
544  return c;
545  }
546  }
547  }
548 
549  //Derived from http://www.libpng.org/pub/png/spec/1.2/PNG-CRCAppendix.html
550  internal static class CRC32
551  {
552  /* Table of CRCs of all 8-bit messages. */
553  private static readonly uint[] crc_table = new uint[256];
554 
555  /* Flag: has the table been computed? Initially false. */
556  private static bool crc_table_computed = false;
557 
558  /* Make the table for a fast CRC. */
559  private static void MakeCRCtable()
560  {
561  uint c;
562  int n, k;
563 
564  for (n = 0; n < 256; n++)
565  {
566  c = (uint)n;
567  for (k = 0; k < 8; k++)
568  {
569  if ((c & 1) != 0)
570  {
571  c = 0xedb88320 ^ (c >> 1);
572  }
573  else
574  {
575  c >>= 1;
576  }
577  }
578  crc_table[n] = c;
579  }
580  crc_table_computed = true;
581  }
582 
583  /* Update a running CRC with the bytes buf[0..len-1]--the CRC
584  should be initialized to all 1's, and the transmitted value
585  is the 1's complement of the final running CRC (see the
586  crc() routine below)). */
587 
588  private static uint UpdateCRC(uint crc, Stream buf)
589  {
590  uint c = crc;
591  int n;
592 
593  if (!crc_table_computed)
594  {
595  MakeCRCtable();
596  }
597 
598  buf.Seek(0, SeekOrigin.Begin);
599 
600  for (n = 0; n < buf.Length; n++)
601  {
602  c = crc_table[(c ^ (byte)buf.ReadByte()) & 0xff] ^ (c >> 8);
603  }
604  return c;
605  }
606 
607  private static uint UpdateCRC(uint crc, byte[] buf, int length)
608  {
609  uint c = crc;
610  int n;
611 
612  if (!crc_table_computed)
613  {
614  MakeCRCtable();
615  }
616 
617  for (n = 0; n < length; n++)
618  {
619  c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
620  }
621  return c;
622  }
623 
624  /* Return the CRC of the bytes buf[0..len-1]. */
625  public static uint ComputeCRC(Stream buf)
626  {
627  return UpdateCRC(0xffffffff, buf) ^ 0xffffffff;
628  }
629 
630  public static uint ComputeCRC(byte[] buf, int length, byte[] prefix)
631  {
632  uint temp = UpdateCRC(0xffffffff, prefix, prefix.Length);
633  return UpdateCRC(temp, buf, length) ^ 0xffffffff;
634  }
635  }
636 
637  internal static class StreamUtils
638  {
639  public static void WriteASCIIString(this Stream sr, string val)
640  {
641  foreach (char c in val)
642  {
643  sr.WriteByte((byte)c);
644  }
645  }
646 
647  internal static MemoryStream ZLibCompress(IntPtr memoryAddress, int memoryLength)
648  {
649  MemoryStream compressedStream = new MemoryStream();
650  compressedStream.Write(new byte[] { 0x78, 0x01 }, 0, 2);
651 
652  byte[] buffer = new byte[1024];
653 
654  AdlerHolder adlerHolder = new AdlerHolder();
655 
656  using (DeflateStream deflate = new DeflateStream(compressedStream, CompressionLevel.Optimal, true))
657  {
658  while (memoryLength > 0)
659  {
660  int currentBytes = Math.Min(memoryLength, 1024);
661  Marshal.Copy(memoryAddress, buffer, 0, currentBytes);
662  deflate.Write(buffer, 0, currentBytes);
663  memoryLength -= currentBytes;
664  memoryAddress = IntPtr.Add(memoryAddress, currentBytes);
665  Adler32(buffer, currentBytes, adlerHolder);
666  }
667  }
668 
669 
670  uint checksum = (adlerHolder.s2 << 16) + adlerHolder.s1;
671 
672  compressedStream.Write(new byte[] { (byte)((checksum >> 24) & 255), (byte)((checksum >> 16) & 255), (byte)((checksum >> 8) & 255), (byte)(checksum & 255) }, 0, 4);
673 
674  compressedStream.Seek(0, SeekOrigin.Begin);
675 
676  return compressedStream;
677  }
678 
679  internal static uint Adler32(Stream contentStream)
680  {
681  uint s1 = 1;
682  uint s2 = 0;
683 
684  int readByte;
685 
686  while ((readByte = contentStream.ReadByte()) >= 0)
687  {
688  s1 = (s1 + (byte)readByte) % 65521U;
689  s2 = (s2 + s1) % 65521U;
690  }
691 
692  return (s2 << 16) + s1;
693  }
694 
695 
696  internal class AdlerHolder
697  {
698  public uint s1;
699  public uint s2;
700 
701  public AdlerHolder()
702  {
703  s1 = 1;
704  s2 = 0;
705  }
706  }
707 
708  internal static void Adler32(byte[] buffer, int length, AdlerHolder holder)
709  {
710  for (int i = 0; i < length; i++)
711  {
712  holder.s1 = (holder.s1 + buffer[i]) % 65521U;
713  holder.s2 = (holder.s2 + holder.s1) % 65521U;
714  }
715  }
716  }
717 }
VectSharp
Definition: Brush.cs:26