@@ -39,11 +39,11 @@ export class ImageRenderer {
3939 * 简单拉伸渲染
4040 */
4141 private static renderSimple (
42- ctx : CanvasRenderingContext2D ,
43- img : HTMLImageElement ,
44- x : number ,
45- y : number ,
46- width : number ,
42+ ctx : CanvasRenderingContext2D ,
43+ img : HTMLImageElement ,
44+ x : number ,
45+ y : number ,
46+ width : number ,
4747 height : number
4848 ) : void {
4949 ctx . drawImage ( img , x , y , width , height ) ;
@@ -53,12 +53,12 @@ export class ImageRenderer {
5353 * 九宫格渲染
5454 */
5555 private static renderSliced (
56- ctx : CanvasRenderingContext2D ,
57- img : HTMLImageElement ,
58- x : number ,
59- y : number ,
60- width : number ,
61- height : number ,
56+ ctx : CanvasRenderingContext2D ,
57+ img : HTMLImageElement ,
58+ x : number ,
59+ y : number ,
60+ width : number ,
61+ height : number ,
6262 inset ?: IInsetParams
6363 ) : void {
6464 if ( ! inset ) {
@@ -71,139 +71,200 @@ export class ImageRenderer {
7171 const imgWidth = img . width ;
7272 const imgHeight = img . height ;
7373
74+ // 先检查原始inset值是否合法
75+ if ( left < 0 || top < 0 || right < 0 || bottom < 0 ) {
76+ console . warn ( '[Layout] inset values cannot be negative, fallback to simple render' ) ;
77+ ImageRenderer . renderSimple ( ctx , img , x , y , width , height ) ;
78+ return ;
79+ }
80+
81+ if ( left + right >= imgWidth || top + bottom >= imgHeight ) {
82+ console . warn ( `[Layout] inset values too large for image size (${ imgWidth } x${ imgHeight } ), fallback to simple render` ) ;
83+ ImageRenderer . renderSimple ( ctx , img , x , y , width , height ) ;
84+ return ;
85+ }
86+
87+ // 确保inset值不超过图片尺寸(此时已经验证过合法性)
88+ const safeLeft = Math . min ( left , imgWidth ) ;
89+ const safeTop = Math . min ( top , imgHeight ) ;
90+ const safeRight = Math . min ( right , imgWidth ) ;
91+ const safeBottom = Math . min ( bottom , imgHeight ) ;
92+
7493 // 计算源区域尺寸
75- const centerSrcWidth = imgWidth - left - right ;
76- const centerSrcHeight = imgHeight - top - bottom ;
94+ const centerSrcWidth = imgWidth - safeLeft - safeRight ;
95+ const centerSrcHeight = imgHeight - safeTop - safeBottom ;
7796
7897 // 计算目标区域尺寸
79- const targetCenterWidth = Math . max ( 0 , width - left - right ) ;
80- const targetCenterHeight = Math . max ( 0 , height - top - bottom ) ;
98+ const targetCenterWidth = Math . max ( 0 , width - safeLeft - safeRight ) ;
99+ const targetCenterHeight = Math . max ( 0 , height - safeTop - safeBottom ) ;
81100
82101 // 1. 渲染四个角(保持原样)
83- ImageRenderer . renderCorners ( ctx , img , x , y , width , height , imgWidth , imgHeight , left , top , right , bottom ) ;
102+ ImageRenderer . renderCorners ( ctx , img , x , y , width , height , imgWidth , imgHeight , safeLeft , safeTop , safeRight , safeBottom ) ;
84103
85104 // 2. 渲染四条边(拉伸)
86- ImageRenderer . renderEdges ( ctx , img , x , y , width , height , imgWidth , imgHeight , left , top , right , bottom , centerSrcWidth , centerSrcHeight , targetCenterWidth , targetCenterHeight ) ;
105+ ImageRenderer . renderEdges ( ctx , img , x , y , width , height , imgWidth , imgHeight , safeLeft , safeTop , safeRight , safeBottom , centerSrcWidth , centerSrcHeight , targetCenterWidth , targetCenterHeight ) ;
87106
88107 // 3. 渲染中心区域(拉伸)
89108 if ( targetCenterWidth > 0 && targetCenterHeight > 0 && centerSrcWidth > 0 && centerSrcHeight > 0 ) {
90- ctx . drawImage ( img , left , top , centerSrcWidth , centerSrcHeight ,
91- x + left , y + top , targetCenterWidth , targetCenterHeight ) ;
109+ ctx . drawImage ( img , safeLeft , safeTop , centerSrcWidth , centerSrcHeight ,
110+ x + safeLeft , y + safeTop , targetCenterWidth , targetCenterHeight ) ;
92111 }
93112 }
94113
95114 /**
96115 * 平铺渲染
97116 */
98117 private static renderTiled (
99- ctx : CanvasRenderingContext2D ,
100- img : HTMLImageElement ,
101- x : number ,
102- y : number ,
103- width : number ,
118+ ctx : CanvasRenderingContext2D ,
119+ img : HTMLImageElement ,
120+ x : number ,
121+ y : number ,
122+ width : number ,
104123 height : number
105124 ) : void {
106- const pattern = ctx . createPattern ( img , 'repeat' ) ;
107- if ( ! pattern ) return ;
125+ const imgWidth = img . width ;
126+ const imgHeight = img . height ;
108127
109128 ctx . save ( ) ;
110-
129+
111130 // 设置裁剪区域
112131 ctx . beginPath ( ) ;
113132 ctx . rect ( x , y , width , height ) ;
114133 ctx . clip ( ) ;
115-
116- // 设置pattern并填充
117- ctx . fillStyle = pattern ;
118- ctx . fillRect ( x , y , width , height ) ;
119-
134+
135+ // 预计算完整块和边界块的数量,避免重复计算
136+ const fullCols = Math . floor ( width / imgWidth ) ;
137+ const fullRows = Math . floor ( height / imgHeight ) ;
138+ const hasPartialCol = width % imgWidth > 0 ;
139+ const hasPartialRow = height % imgHeight > 0 ;
140+
141+ // 1. 优先绘制所有完整的块(最常见的情况,无需边界计算)
142+ for ( let row = 0 ; row < fullRows ; row ++ ) {
143+ const drawY = y + row * imgHeight ;
144+ for ( let col = 0 ; col < fullCols ; col ++ ) {
145+ const drawX = x + col * imgWidth ;
146+ ctx . drawImage ( img , drawX , drawY ) ;
147+ }
148+ }
149+
150+ // 2. 处理右边界的部分列(如果存在)
151+ if ( hasPartialCol ) {
152+ const partialWidth = width - fullCols * imgWidth ;
153+ const partialColX = x + fullCols * imgWidth ;
154+
155+ // 完整行的部分列
156+ for ( let row = 0 ; row < fullRows ; row ++ ) {
157+ const drawY = y + row * imgHeight ;
158+ ctx . drawImage ( img , 0 , 0 , partialWidth , imgHeight , partialColX , drawY , partialWidth , imgHeight ) ;
159+ }
160+
161+ // 右下角的部分块(如果底边界也是部分的)
162+ if ( hasPartialRow ) {
163+ const partialHeight = height - fullRows * imgHeight ;
164+ const partialRowY = y + fullRows * imgHeight ;
165+ ctx . drawImage ( img , 0 , 0 , partialWidth , partialHeight , partialColX , partialRowY , partialWidth , partialHeight ) ;
166+ }
167+ }
168+
169+ // 3. 处理底边界的部分行(如果存在)
170+ if ( hasPartialRow ) {
171+ const partialHeight = height - fullRows * imgHeight ;
172+ const partialRowY = y + fullRows * imgHeight ;
173+
174+ // 完整列的部分行
175+ for ( let col = 0 ; col < fullCols ; col ++ ) {
176+ const drawX = x + col * imgWidth ;
177+ ctx . drawImage ( img , 0 , 0 , imgWidth , partialHeight , drawX , partialRowY , imgWidth , partialHeight ) ;
178+ }
179+ }
180+
120181 ctx . restore ( ) ;
121182 }
122183
123184 /**
124185 * 渲染九宫格的四个角
125186 */
126187 private static renderCorners (
127- ctx : CanvasRenderingContext2D ,
128- img : HTMLImageElement ,
129- x : number ,
130- y : number ,
131- width : number ,
132- height : number ,
133- imgWidth : number ,
134- imgHeight : number ,
135- left : number ,
136- top : number ,
137- right : number ,
188+ ctx : CanvasRenderingContext2D ,
189+ img : HTMLImageElement ,
190+ x : number ,
191+ y : number ,
192+ width : number ,
193+ height : number ,
194+ imgWidth : number ,
195+ imgHeight : number ,
196+ left : number ,
197+ top : number ,
198+ right : number ,
138199 bottom : number
139200 ) : void {
140201 // 左上角
141202 if ( left > 0 && top > 0 ) {
142203 ctx . drawImage ( img , 0 , 0 , left , top , x , y , left , top ) ;
143204 }
144-
205+
145206 // 右上角
146207 if ( right > 0 && top > 0 ) {
147- ctx . drawImage ( img , imgWidth - right , 0 , right , top ,
148- x + width - right , y , right , top ) ;
208+ ctx . drawImage ( img , imgWidth - right , 0 , right , top ,
209+ x + width - right , y , right , top ) ;
149210 }
150-
211+
151212 // 左下角
152213 if ( left > 0 && bottom > 0 ) {
153- ctx . drawImage ( img , 0 , imgHeight - bottom , left , bottom ,
154- x , y + height - bottom , left , bottom ) ;
214+ ctx . drawImage ( img , 0 , imgHeight - bottom , left , bottom ,
215+ x , y + height - bottom , left , bottom ) ;
155216 }
156-
217+
157218 // 右下角
158219 if ( right > 0 && bottom > 0 ) {
159220 ctx . drawImage ( img , imgWidth - right , imgHeight - bottom , right , bottom ,
160- x + width - right , y + height - bottom , right , bottom ) ;
221+ x + width - right , y + height - bottom , right , bottom ) ;
161222 }
162223 }
163224
164225 /**
165226 * 渲染九宫格的四条边
166227 */
167228 private static renderEdges (
168- ctx : CanvasRenderingContext2D ,
169- img : HTMLImageElement ,
170- x : number ,
171- y : number ,
172- width : number ,
173- height : number ,
174- imgWidth : number ,
175- imgHeight : number ,
176- left : number ,
177- top : number ,
178- right : number ,
179- bottom : number ,
180- centerSrcWidth : number ,
181- centerSrcHeight : number ,
182- targetCenterWidth : number ,
229+ ctx : CanvasRenderingContext2D ,
230+ img : HTMLImageElement ,
231+ x : number ,
232+ y : number ,
233+ width : number ,
234+ height : number ,
235+ imgWidth : number ,
236+ imgHeight : number ,
237+ left : number ,
238+ top : number ,
239+ right : number ,
240+ bottom : number ,
241+ centerSrcWidth : number ,
242+ centerSrcHeight : number ,
243+ targetCenterWidth : number ,
183244 targetCenterHeight : number
184245 ) : void {
185246 // 上边 - 水平拉伸
186247 if ( top > 0 && targetCenterWidth > 0 ) {
187248 ctx . drawImage ( img , left , 0 , centerSrcWidth , top ,
188- x + left , y , targetCenterWidth , top ) ;
249+ x + left , y , targetCenterWidth , top ) ;
189250 }
190-
251+
191252 // 下边 - 水平拉伸
192253 if ( bottom > 0 && targetCenterWidth > 0 ) {
193254 ctx . drawImage ( img , left , imgHeight - bottom , centerSrcWidth , bottom ,
194- x + left , y + height - bottom , targetCenterWidth , bottom ) ;
255+ x + left , y + height - bottom , targetCenterWidth , bottom ) ;
195256 }
196-
257+
197258 // 左边 - 垂直拉伸
198259 if ( left > 0 && targetCenterHeight > 0 ) {
199260 ctx . drawImage ( img , 0 , top , left , centerSrcHeight ,
200- x , y + top , left , targetCenterHeight ) ;
261+ x , y + top , left , targetCenterHeight ) ;
201262 }
202-
263+
203264 // 右边 - 垂直拉伸
204265 if ( right > 0 && targetCenterHeight > 0 ) {
205266 ctx . drawImage ( img , imgWidth - right , top , right , centerSrcHeight ,
206- x + width - right , y + top , right , targetCenterHeight ) ;
267+ x + width - right , y + top , right , targetCenterHeight ) ;
207268 }
208269 }
209270}
0 commit comments