@@ -33,7 +33,6 @@ private static bool IsValidColorSpace(IPdfImage pdfImage)
33
33
&& pdfImage . ColorSpaceDetails ! . BaseType != ColorSpace . Pattern ;
34
34
}
35
35
36
-
37
36
private static bool IsImageArrayCorrectlySized ( IPdfImage pdfImage , ReadOnlySpan < byte > bytesPure )
38
37
{
39
38
var actualSize = bytesPure . Length ;
@@ -52,6 +51,23 @@ private static bool IsImageArrayCorrectlySized(IPdfImage pdfImage, ReadOnlySpan<
52
51
bytesPure [ actualSize - 1 ] == ReadHelper . AsciiLineFeed ) ;
53
52
}
54
53
54
+ private static bool HasAlphaChannel ( this IPdfImage pdfImage )
55
+ {
56
+ return pdfImage . MaskImage is not null || pdfImage . ImageDictionary . ContainsKey ( NameToken . Mask ) ;
57
+ }
58
+
59
+ public static int GetRasterSize ( this IPdfImage pdfImage )
60
+ {
61
+ int width = pdfImage . WidthInSamples ;
62
+ int height = pdfImage . HeightInSamples ;
63
+
64
+ var numberOfComponents = pdfImage . ColorSpaceDetails ! . BaseNumberOfColorComponents ;
65
+ bool isRgba = numberOfComponents > 1 || pdfImage . HasAlphaChannel ( ) ;
66
+ int bytesPerPixel = isRgba ? 4 : 1 ; // 3 (RGB) + 1 (alpha)
67
+
68
+ return height * width * bytesPerPixel ;
69
+ }
70
+
55
71
// https://stackoverflow.com/questions/50312937/skiasharp-tiff-support#50370515
56
72
private static bool TryGenerate ( this IPdfImage pdfImage , out SKImage skImage )
57
73
{
@@ -65,7 +81,7 @@ private static bool TryGenerate(this IPdfImage pdfImage, out SKImage skImage)
65
81
var bytesPure = imageMemory . Span ;
66
82
SKImage ? mask = null ;
67
83
SKPixmap ? sMaskPixmap = null ;
68
-
84
+
69
85
try
70
86
{
71
87
int width = pdfImage . WidthInSamples ;
@@ -81,79 +97,71 @@ private static bool TryGenerate(this IPdfImage pdfImage, out SKImage skImage)
81
97
return false ;
82
98
}
83
99
84
- if ( numberOfComponents == 1 )
100
+ bool isRgba = numberOfComponents > 1 || pdfImage . HasAlphaChannel ( ) ;
101
+ var colorSpace = isRgba ? SKColorType . Rgba8888 : SKColorType . Gray8 ;
102
+ /*
103
+ if (pdfImage.IsImageMask && colorSpace == SKColorType.Gray8)
85
104
{
86
- if ( pdfImage . MaskImage is not null )
87
- {
88
- // TODO
89
- }
90
-
91
- return TryGetGray8Bitmap ( width , height , bytesPure , out skImage ) ;
105
+ colorSpace = SKColorType.Alpha8;
92
106
}
107
+ */
93
108
94
109
// We apparently need SKAlphaType.Unpremul to avoid artifacts with transparency.
95
110
// For example, the logo's background in "Motor Insurance claim form.pdf" might
96
111
// appear black instead of transparent at certain scales.
97
112
// See https://groups.google.com/g/skia-discuss/c/sV6e3dpf4CE for related question
98
- var info = new SKImageInfo ( width , height , SKColorType . Rgba8888 , SKAlphaType . Unpremul ) ;
99
-
100
- // create the buffer that will hold the pixels
101
- const int bytesPerPixel = 4 ; // 3 (RGB) + 1 (alpha)
102
-
103
- var length = ( height * width * bytesPerPixel ) + height ;
113
+ var info = new SKImageInfo ( width , height , colorSpace , SKAlphaType . Unpremul ) ;
104
114
105
- var raster = new byte [ length ] ;
106
-
107
- // get a pointer to the buffer, and give it to the skImage
108
- var ptr = GCHandle . Alloc ( raster , GCHandleType . Pinned ) ;
109
- using ( SKPixmap pixmap = new SKPixmap ( info , ptr . AddrOfPinnedObject ( ) , info . RowBytes ) )
110
- {
111
- skImage = SKImage . FromPixels ( pixmap , ( addr , ctx ) => ptr . Free ( ) ) ;
112
- }
115
+ int bytesPerPixel = isRgba ? 4 : 1 ; // 3 (RGB) + 1 (alpha)
113
116
114
117
Func < int , int , byte , byte , byte , byte > getAlphaChannel = ( _ , _ , _ , _ , _ ) => byte . MaxValue ;
115
- if ( pdfImage . MaskImage ? . TryGenerate ( out mask ) == true )
118
+ if ( pdfImage . MaskImage is not null )
116
119
{
117
- if ( ! skImage . Info . Rect . Equals ( mask ! . Info . Rect ) )
120
+ if ( pdfImage . MaskImage . TryGenerate ( out mask ) )
118
121
{
119
- // Resize
120
- var maskInfo = new SKImageInfo ( skImage . Info . Width , skImage . Info . Height , SKColorType . Gray8 , SKAlphaType . Unpremul ) ;
121
- var maskRaster = new byte [ skImage . Info . Width * skImage . Info . Height ] ;
122
- var ptrMask = GCHandle . Alloc ( maskRaster , GCHandleType . Pinned ) ;
123
- sMaskPixmap = new SKPixmap ( maskInfo , ptrMask . AddrOfPinnedObject ( ) , maskInfo . RowBytes ) ;
124
- if ( ! mask . ScalePixels ( sMaskPixmap , SKFilterQuality . High ) )
122
+ if ( ! info . Rect . Equals ( mask ! . Info . Rect ) )
125
123
{
126
- // TODO - Error
124
+ // Resize
125
+ var maskInfo = new SKImageInfo ( info . Width , info . Height , mask ! . Info . ColorType , mask ! . Info . AlphaType ) ;
126
+ var maskRasterResize = new byte [ info . Width * info . Height ] ;
127
+ var ptrMask = GCHandle . Alloc ( maskRasterResize , GCHandleType . Pinned ) ;
128
+ sMaskPixmap = new SKPixmap ( maskInfo , ptrMask . AddrOfPinnedObject ( ) , maskInfo . RowBytes ) ;
129
+ if ( ! mask . ScalePixels ( sMaskPixmap , SKFilterQuality . High ) )
130
+ {
131
+ // TODO - Error
132
+ }
133
+
134
+ mask . Dispose ( ) ;
135
+
136
+ mask = SKImage . FromPixels ( sMaskPixmap , ( addr , ctx ) => ptrMask . Free ( ) ) ;
137
+ }
138
+ else
139
+ {
140
+ sMaskPixmap = mask . PeekPixels ( ) ;
127
141
}
128
142
129
- mask . Dispose ( ) ;
130
- mask = SKImage . FromPixels ( sMaskPixmap , ( addr , ctx ) => ptrMask . Free ( ) ) ;
131
- }
132
- else
133
- {
134
- sMaskPixmap = mask . PeekPixels ( ) ;
135
- }
136
-
137
- if ( ! sMaskPixmap . GetPixelSpan ( ) . IsEmpty )
138
- {
139
- //getAlphaChannel = (row, col, _, _, _) => sMaskPixmap.GetPixelSpan()[(row * width) + col];
140
-
141
- if ( pdfImage . MaskImage . NeedsReverseDecode ( ) )
143
+ if ( ! sMaskPixmap . GetPixelSpan ( ) . IsEmpty )
142
144
{
145
+ // TODO - It is very unclear why we need this logic of reversing or not here for IsImageMask
146
+ if ( pdfImage . MaskImage . IsImageMask )
147
+ {
148
+ // We reverse pixel color
149
+ getAlphaChannel = ( row , col , _ , _ , _ ) => ( byte ) ~ sMaskPixmap . GetPixelSpan ( ) [ ( row * width ) + col ] ;
150
+ }
151
+ else
152
+ {
153
+ // This is a NameToken.Smask - we do not reverse pixel color
154
+ getAlphaChannel = ( row , col , _ , _ , _ ) => sMaskPixmap . GetPixelSpan ( ) [ ( row * width ) + col ] ;
155
+ }
156
+
157
+ // Examples of docs that are decode inverse
143
158
// MOZILLA-LINK-3264-0.pdf
144
159
// MOZILLA-LINK-4246-2.pdf
145
160
// MOZILLA-LINK-4293-0.pdf
146
161
// MOZILLA-LINK-4314-0.pdf
147
162
// MOZILLA-LINK-3758-0.pdf
148
163
149
- // Wrong: MOZILLA-LINK-4379-0.pd
150
-
151
- getAlphaChannel = ( row , col , _ , _ , _ ) =>
152
- Convert . ToByte ( 255 - sMaskPixmap . GetPixelSpan ( ) [ ( row * width ) + col ] ) ;
153
- }
154
- else
155
- {
156
- getAlphaChannel = ( row , col , _ , _ , _ ) => sMaskPixmap . GetPixelSpan ( ) [ ( row * width ) + col ] ;
164
+ // Wrong: MOZILLA-LINK-4379-0.pdf
157
165
}
158
166
}
159
167
}
@@ -200,6 +208,10 @@ private static bool TryGenerate(this IPdfImage pdfImage, out SKImage skImage)
200
208
gMax = range [ 4 ] ;
201
209
bMax = range [ 5 ] ;
202
210
}
211
+ else if ( numberOfComponents == 1 )
212
+ {
213
+ throw new NotImplementedException ( "Mask with numberOfComponents == 1." ) ;
214
+ }
203
215
204
216
getAlphaChannel = ( _ , _ , r , g , b ) =>
205
217
{
@@ -214,7 +226,23 @@ private static bool TryGenerate(this IPdfImage pdfImage, out SKImage skImage)
214
226
} ;
215
227
}
216
228
217
- if ( pdfImage . ColorSpaceDetails . BaseType == ColorSpace . DeviceCMYK || numberOfComponents == 4 )
229
+ // create the buffer that will hold the pixels
230
+ byte [ ] raster = new byte [ height * width * bytesPerPixel ] ;
231
+ Span < byte > rasterSpan = raster ;
232
+
233
+ // get a pointer to the buffer, and give it to the skImage
234
+ var ptr = GCHandle . Alloc ( raster , GCHandleType . Pinned ) ;
235
+ using ( SKPixmap pixmap = new SKPixmap ( info , ptr . AddrOfPinnedObject ( ) , info . RowBytes ) )
236
+ {
237
+ skImage = SKImage . FromPixels ( pixmap , ( addr , ctx ) =>
238
+ {
239
+ ptr . Free ( ) ;
240
+ raster = null ;
241
+ System . Diagnostics . Debug . WriteLine ( "ptr.Free()" ) ;
242
+ } ) ;
243
+ }
244
+
245
+ if ( numberOfComponents == 4 )
218
246
{
219
247
int i = 0 ;
220
248
for ( int row = 0 ; row < height ; ++ row )
@@ -225,10 +253,10 @@ private static bool TryGenerate(this IPdfImage pdfImage, out SKImage skImage)
225
253
out byte r , out byte g , out byte b ) ;
226
254
227
255
var start = ( row * ( width * bytesPerPixel ) ) + ( col * bytesPerPixel ) ;
228
- raster [ start ++ ] = r ;
229
- raster [ start ++ ] = g ;
230
- raster [ start ++ ] = b ;
231
- raster [ start ] = getAlphaChannel ( row , col , r , g , b ) ;
256
+ rasterSpan [ start ] = r ;
257
+ rasterSpan [ start + 1 ] = g ;
258
+ rasterSpan [ start + 2 ] = b ;
259
+ rasterSpan [ start + 3 ] = getAlphaChannel ( row , col , r , g , b ) ;
232
260
}
233
261
}
234
262
@@ -247,11 +275,52 @@ private static bool TryGenerate(this IPdfImage pdfImage, out SKImage skImage)
247
275
byte b = bytesPure [ i ++ ] ;
248
276
249
277
var start = ( row * ( width * bytesPerPixel ) ) + ( col * bytesPerPixel ) ;
250
- raster [ start ++ ] = r ;
251
- raster [ start ++ ] = g ;
252
- raster [ start ++ ] = b ;
253
- raster [ start ] = getAlphaChannel ( row , col , r , g , b ) ;
278
+ rasterSpan [ start ] = r ;
279
+ rasterSpan [ start + 1 ] = g ;
280
+ rasterSpan [ start + 2 ] = b ;
281
+ rasterSpan [ start + 3 ] = getAlphaChannel ( row , col , r , g , b ) ;
282
+ }
283
+ }
284
+
285
+ return true ;
286
+ }
287
+
288
+ if ( numberOfComponents == 1 )
289
+ {
290
+ if ( isRgba )
291
+ {
292
+ // Handle gray scale image as RGBA because we have an alpha channel
293
+ int i = 0 ;
294
+ for ( int row = 0 ; row < height ; ++ row )
295
+ {
296
+ for ( int col = 0 ; col < width ; ++ col )
297
+ {
298
+ byte g = bytesPure [ i ++ ] ;
299
+
300
+ var start = ( row * ( width * bytesPerPixel ) ) + ( col * bytesPerPixel ) ;
301
+ rasterSpan [ start ] = g ;
302
+ rasterSpan [ start + 1 ] = g ;
303
+ rasterSpan [ start + 2 ] = g ;
304
+ rasterSpan [ start + 3 ] = getAlphaChannel ( row , col , g , g , g ) ;
305
+ }
254
306
}
307
+
308
+ return true ;
309
+ }
310
+
311
+ if ( pdfImage . NeedsReverseDecode ( ) )
312
+ {
313
+ for ( int i = 0 ; i < bytesPure . Length ; ++ i )
314
+ {
315
+ rasterSpan [ i ] = ( byte ) ~ bytesPure [ i ] ;
316
+ }
317
+
318
+ return true ;
319
+ }
320
+
321
+ for ( int i = 0 ; i < bytesPure . Length ; ++ i )
322
+ {
323
+ rasterSpan [ i ] = bytesPure [ i ] ;
255
324
}
256
325
257
326
return true ;
@@ -312,7 +381,6 @@ private static bool TryGetGray8Bitmap(int width, int height, ReadOnlySpan<byte>
312
381
313
382
public static SKImage GetSKImage ( this IPdfImage pdfImage )
314
383
{
315
- // Try get png bytes
316
384
if ( pdfImage . TryGenerate ( out var bitmap ) )
317
385
{
318
386
return bitmap ;
0 commit comments