13
13
* limitations under the License.
14
14
*/
15
15
16
- import { assert , FormatError , ImageKind , info , warn } from "../shared/util.js" ;
17
- import { applyMaskImageData } from "../shared/image_utils.js" ;
16
+ import {
17
+ assert ,
18
+ FeatureTest ,
19
+ FormatError ,
20
+ ImageKind ,
21
+ info ,
22
+ warn ,
23
+ } from "../shared/util.js" ;
24
+ import {
25
+ convertBlackAndWhiteToRGBA ,
26
+ convertToRGBA ,
27
+ } from "../shared/image_utils.js" ;
18
28
import { BaseStream } from "./base_stream.js" ;
19
29
import { ColorSpace } from "./colorspace.js" ;
20
30
import { DecodeStream } from "./decode_stream.js" ;
@@ -364,11 +374,12 @@ class PDFImage {
364
374
const canvas = new OffscreenCanvas ( width , height ) ;
365
375
const ctx = canvas . getContext ( "2d" ) ;
366
376
const imgData = ctx . createImageData ( width , height ) ;
367
- applyMaskImageData ( {
377
+ convertBlackAndWhiteToRGBA ( {
368
378
src : imgArray ,
369
379
dest : imgData . data ,
370
380
width,
371
381
height,
382
+ nonBlackColor : 0 ,
372
383
inverseDecode,
373
384
} ) ;
374
385
@@ -641,7 +652,7 @@ class PDFImage {
641
652
}
642
653
}
643
654
644
- createImageData ( forceRGBA = false ) {
655
+ createImageData ( forceRGBA = false , isOffscreenCanvasSupported = false ) {
645
656
const drawWidth = this . drawWidth ;
646
657
const drawHeight = this . drawHeight ;
647
658
const imgData = {
@@ -686,8 +697,12 @@ class PDFImage {
686
697
drawWidth === originalWidth &&
687
698
drawHeight === originalHeight
688
699
) {
700
+ const data = this . getImageBytes ( originalHeight * rowBytes , { } ) ;
701
+ if ( isOffscreenCanvasSupported ) {
702
+ return this . createBitmap ( kind , originalWidth , originalHeight , data ) ;
703
+ }
689
704
imgData . kind = kind ;
690
- imgData . data = this . getImageBytes ( originalHeight * rowBytes , { } ) ;
705
+ imgData . data = data ;
691
706
692
707
if ( this . needsDecode ) {
693
708
// Invert the buffer (which must be grayscale if we reached here).
@@ -704,21 +719,52 @@ class PDFImage {
704
719
}
705
720
if ( this . image instanceof JpegStream && ! this . smask && ! this . mask ) {
706
721
let imageLength = originalHeight * rowBytes ;
707
- switch ( this . colorSpace . name ) {
708
- case "DeviceGray" :
709
- // Avoid truncating the image, since `JpegImage.getData`
710
- // will expand the image data when `forceRGB === true`.
711
- imageLength *= 3 ;
712
- /* falls through */
713
- case "DeviceRGB" :
714
- case "DeviceCMYK" :
715
- imgData . kind = ImageKind . RGB_24BPP ;
716
- imgData . data = this . getImageBytes ( imageLength , {
722
+ if ( isOffscreenCanvasSupported ) {
723
+ let isHandled = false ;
724
+ switch ( this . colorSpace . name ) {
725
+ case "DeviceGray" :
726
+ // Avoid truncating the image, since `JpegImage.getData`
727
+ // will expand the image data when `forceRGB === true`.
728
+ imageLength *= 4 ;
729
+ isHandled = true ;
730
+ break ;
731
+ case "DeviceRGB" :
732
+ imageLength = ( imageLength / 3 ) * 4 ;
733
+ isHandled = true ;
734
+ break ;
735
+ case "DeviceCMYK" :
736
+ isHandled = true ;
737
+ break ;
738
+ }
739
+
740
+ if ( isHandled ) {
741
+ const rgba = this . getImageBytes ( imageLength , {
717
742
drawWidth,
718
743
drawHeight,
719
- forceRGB : true ,
744
+ forceRGBA : true ,
720
745
} ) ;
721
- return imgData ;
746
+ return this . createBitmap (
747
+ ImageKind . RGBA_32BPP ,
748
+ drawWidth ,
749
+ drawHeight ,
750
+ rgba
751
+ ) ;
752
+ }
753
+ } else {
754
+ switch ( this . colorSpace . name ) {
755
+ case "DeviceGray" :
756
+ imageLength *= 3 ;
757
+ /* falls through */
758
+ case "DeviceRGB" :
759
+ case "DeviceCMYK" :
760
+ imgData . kind = ImageKind . RGB_24BPP ;
761
+ imgData . data = this . getImageBytes ( imageLength , {
762
+ drawWidth,
763
+ drawHeight,
764
+ forceRGB : true ,
765
+ } ) ;
766
+ return imgData ;
767
+ }
722
768
}
723
769
}
724
770
}
@@ -735,32 +781,45 @@ class PDFImage {
735
781
// If opacity data is present, use RGBA_32BPP form. Otherwise, use the
736
782
// more compact RGB_24BPP form if allowable.
737
783
let alpha01 , maybeUndoPreblend ;
784
+
785
+ let canvas , ctx , canvasImgData , data ;
786
+ if ( isOffscreenCanvasSupported ) {
787
+ canvas = new OffscreenCanvas ( drawWidth , drawHeight ) ;
788
+ ctx = canvas . getContext ( "2d" ) ;
789
+ canvasImgData = ctx . createImageData ( drawWidth , drawHeight ) ;
790
+ data = canvasImgData . data ;
791
+ }
792
+
793
+ imgData . kind = ImageKind . RGBA_32BPP ;
794
+
738
795
if ( ! forceRGBA && ! this . smask && ! this . mask ) {
739
- imgData . kind = ImageKind . RGB_24BPP ;
740
- imgData . data = new Uint8ClampedArray ( drawWidth * drawHeight * 3 ) ;
741
- alpha01 = 0 ;
796
+ if ( ! isOffscreenCanvasSupported ) {
797
+ imgData . kind = ImageKind . RGB_24BPP ;
798
+ data = new Uint8ClampedArray ( drawWidth * drawHeight * 3 ) ;
799
+ alpha01 = 0 ;
800
+ } else {
801
+ const arr = new Uint32Array ( data . buffer ) ;
802
+ arr . fill ( FeatureTest . isLittleEndian ? 0xff000000 : 0x000000ff ) ;
803
+ alpha01 = 1 ;
804
+ }
742
805
maybeUndoPreblend = false ;
743
806
} else {
744
- imgData . kind = ImageKind . RGBA_32BPP ;
745
- imgData . data = new Uint8ClampedArray ( drawWidth * drawHeight * 4 ) ;
807
+ if ( ! isOffscreenCanvasSupported ) {
808
+ data = new Uint8ClampedArray ( drawWidth * drawHeight * 4 ) ;
809
+ }
810
+
746
811
alpha01 = 1 ;
747
812
maybeUndoPreblend = true ;
748
813
749
814
// Color key masking (opacity) must be performed before decoding.
750
- this . fillOpacity (
751
- imgData . data ,
752
- drawWidth ,
753
- drawHeight ,
754
- actualHeight ,
755
- comps
756
- ) ;
815
+ this . fillOpacity ( data , drawWidth , drawHeight , actualHeight , comps ) ;
757
816
}
758
817
759
818
if ( this . needsDecode ) {
760
819
this . decodeBuffer ( comps ) ;
761
820
}
762
821
this . colorSpace . fillRgb (
763
- imgData . data ,
822
+ data ,
764
823
originalWidth ,
765
824
originalHeight ,
766
825
drawWidth ,
@@ -771,9 +830,23 @@ class PDFImage {
771
830
alpha01
772
831
) ;
773
832
if ( maybeUndoPreblend ) {
774
- this . undoPreblend ( imgData . data , drawWidth , actualHeight ) ;
833
+ this . undoPreblend ( data , drawWidth , actualHeight ) ;
775
834
}
776
835
836
+ if ( isOffscreenCanvasSupported ) {
837
+ ctx . putImageData ( canvasImgData , 0 , 0 ) ;
838
+ const bitmap = canvas . transferToImageBitmap ( ) ;
839
+
840
+ return {
841
+ data : null ,
842
+ width : drawWidth ,
843
+ height : drawHeight ,
844
+ bitmap,
845
+ interpolate : this . interpolate ,
846
+ } ;
847
+ }
848
+
849
+ imgData . data = data ;
777
850
return imgData ;
778
851
}
779
852
@@ -833,13 +906,49 @@ class PDFImage {
833
906
}
834
907
}
835
908
909
+ createBitmap ( kind , width , height , src ) {
910
+ const canvas = new OffscreenCanvas ( width , height ) ;
911
+ const ctx = canvas . getContext ( "2d" ) ;
912
+ let imgData ;
913
+ if ( kind === ImageKind . RGBA_32BPP ) {
914
+ imgData = new ImageData ( src , width , height ) ;
915
+ } else {
916
+ imgData = ctx . createImageData ( width , height ) ;
917
+ convertToRGBA ( {
918
+ kind,
919
+ src,
920
+ dest : new Uint32Array ( imgData . data . buffer ) ,
921
+ width,
922
+ height,
923
+ inverseDecode : this . needsDecode ,
924
+ } ) ;
925
+ }
926
+ ctx . putImageData ( imgData , 0 , 0 ) ;
927
+ const bitmap = canvas . transferToImageBitmap ( ) ;
928
+
929
+ return {
930
+ data : null ,
931
+ width,
932
+ height,
933
+ bitmap,
934
+ interpolate : this . interpolate ,
935
+ } ;
936
+ }
937
+
836
938
getImageBytes (
837
939
length ,
838
- { drawWidth, drawHeight, forceRGB = false , internal = false }
940
+ {
941
+ drawWidth,
942
+ drawHeight,
943
+ forceRGBA = false ,
944
+ forceRGB = false ,
945
+ internal = false ,
946
+ }
839
947
) {
840
948
this . image . reset ( ) ;
841
949
this . image . drawWidth = drawWidth || this . width ;
842
950
this . image . drawHeight = drawHeight || this . height ;
951
+ this . image . forceRGBA = ! ! forceRGBA ;
843
952
this . image . forceRGB = ! ! forceRGB ;
844
953
const imageBytes = this . image . getBytes ( length ) ;
845
954
0 commit comments