@@ -6,6 +6,7 @@ import TextLayer from './TextLayer';
66import Constants from '../../constants' ;
77import styled from 'styled-components' ;
88import LoadingPhrase from '../misc/LoadingPhrase' ;
9+ import memoize from "memoize-one" ;
910
1011const Root = styled . div `
1112 position: relative;
@@ -47,11 +48,82 @@ class ComplexImage extends React.PureComponent {
4748 textZones : PropTypes . array ,
4849 rotation : PropTypes . oneOf ( [ 0 , 90 , 180 , 270 ] ) ,
4950 outerRef : PropTypes . func ,
51+ currentPageNumber : PropTypes . number ,
5052 } ;
5153
54+ /**
55+ * Firefox cannot putImageData() bigger than about 12_000 * 12_000 pixels.
56+ * createImageBitmap() fails too.
57+ * Chrome fails on 25_000 * 22_000.
58+ * So the solution is just to scale down an image once, as if it were of a smaller size.
59+ * The current solution should be replaced with createImageBitmap() scaling
60+ * once it's supported by Firefox and Safari. Now images up to 20K * 20K pixels are supported:
61+ * the image is downscaled to half being divided into 4 quarters, using 4 canvases.
62+ * Theoretically, the image can be split into more than 4 equal blocks, so that even bigger ones are processed.
63+ * But there is a doubt the library will be able to decode bigger images,
64+ * since it sometimes fails with an out of memory error on a 16K * 12K image.
65+ */
66+ resizeImageIfRequired = memoize ( ( image ) => {
67+ const dimensionThreshold = 10000 ;
68+ const { width, height } = image ;
69+ const maxDimension = Math . max ( width , height ) ;
70+
71+ if ( maxDimension <= dimensionThreshold ) return image ;
72+
73+ const createTempCanvas = ( image , x , y , width , height ) => {
74+ const canvas = document . createElement ( 'canvas' ) ;
75+ canvas . width = width ;
76+ canvas . height = height ;
77+ const ctx = canvas . getContext ( '2d' , { alpha : false } ) ;
78+ ctx . putImageData ( image , - x , - y , x , y , width , height ) ;
79+ return canvas ;
80+ } ;
81+
82+ const outputCanvas = document . createElement ( 'canvas' ) ;
83+ const outputCtx = outputCanvas . getContext ( '2d' , { alpha : false } ) ;
84+
85+ const halfWidth = Math . floor ( width / 2 ) ;
86+ const halfHeight = Math . floor ( height / 2 ) ;
87+
88+ const width1 = Math . floor ( halfWidth / 2 ) ;
89+ const width2 = Math . floor ( ( width - halfWidth ) / 2 ) ;
90+ const height1 = Math . floor ( halfHeight / 2 ) ;
91+ const height2 = Math . floor ( ( height - halfHeight ) / 2 ) ;
92+
93+ outputCanvas . width = width1 + width2 ;
94+ outputCanvas . height = height1 + height2 ;
95+
96+ const drawImage = ( x , y , width , height , destX , destY , destWidth , destHeight ) => outputCtx . drawImage (
97+ createTempCanvas ( image , x , y , width , height ) ,
98+ 0 , 0 , width , height ,
99+ destX , destY , destWidth , destHeight ,
100+ ) ;
101+
102+ drawImage (
103+ 0 , 0 , halfWidth , halfHeight ,
104+ 0 , 0 , width1 , height1 ,
105+ ) ;
106+ drawImage (
107+ halfWidth , 0 , width - halfWidth , halfHeight ,
108+ width1 , 0 , width2 , height1 ,
109+ ) ;
110+ drawImage (
111+ 0 , halfHeight , halfWidth , height - halfHeight ,
112+ 0 , height1 , width1 , height2 ,
113+ ) ;
114+ drawImage (
115+ halfWidth , halfHeight , width - halfWidth , height - halfHeight ,
116+ width1 , height1 , width2 , height2 ,
117+ ) ;
118+
119+ return outputCtx . getImageData ( 0 , 0 , outputCanvas . width , outputCanvas . height ) ;
120+ } ) ;
121+
52122 render ( ) {
53- const initialWidth = this . props . imageWidth || this . props . imageData . width ;
54- const initialHeight = this . props . imageHeight || this . props . imageData . height ;
123+ const imageData = this . props . imageData && this . resizeImageIfRequired ( this . props . imageData ) ;
124+
125+ const initialWidth = this . props . imageWidth || imageData . width ;
126+ const initialHeight = this . props . imageHeight || imageData . height ;
55127
56128 const scaleFactor = Constants . DEFAULT_DPI / this . props . imageDpi * this . props . userScale ;
57129
@@ -73,8 +145,13 @@ class ComplexImage extends React.PureComponent {
73145 ref = { this . props . outerRef }
74146 >
75147 < div >
76- { this . props . imageData ?
77- < CanvasImage { ...this . props } /> :
148+ { imageData ?
149+ < CanvasImage
150+ imageData = { imageData }
151+ imageDpi = { this . props . imageDpi }
152+ userScale = { this . props . userScale }
153+ key = { this . props . currentPageNumber }
154+ /> :
78155 this . props . imageUrl ?
79156 < img
80157 src = { this . props . imageUrl }
0 commit comments