@@ -39,11 +39,11 @@ export class ImageRenderer {
39
39
* 简单拉伸渲染
40
40
*/
41
41
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 ,
47
47
height : number
48
48
) : void {
49
49
ctx . drawImage ( img , x , y , width , height ) ;
@@ -53,12 +53,12 @@ export class ImageRenderer {
53
53
* 九宫格渲染
54
54
*/
55
55
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 ,
62
62
inset ?: IInsetParams
63
63
) : void {
64
64
if ( ! inset ) {
@@ -71,139 +71,200 @@ export class ImageRenderer {
71
71
const imgWidth = img . width ;
72
72
const imgHeight = img . height ;
73
73
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
+
74
93
// 计算源区域尺寸
75
- const centerSrcWidth = imgWidth - left - right ;
76
- const centerSrcHeight = imgHeight - top - bottom ;
94
+ const centerSrcWidth = imgWidth - safeLeft - safeRight ;
95
+ const centerSrcHeight = imgHeight - safeTop - safeBottom ;
77
96
78
97
// 计算目标区域尺寸
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 ) ;
81
100
82
101
// 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 ) ;
84
103
85
104
// 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 ) ;
87
106
88
107
// 3. 渲染中心区域(拉伸)
89
108
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 ) ;
92
111
}
93
112
}
94
113
95
114
/**
96
115
* 平铺渲染
97
116
*/
98
117
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 ,
104
123
height : number
105
124
) : void {
106
- const pattern = ctx . createPattern ( img , 'repeat' ) ;
107
- if ( ! pattern ) return ;
125
+ const imgWidth = img . width ;
126
+ const imgHeight = img . height ;
108
127
109
128
ctx . save ( ) ;
110
-
129
+
111
130
// 设置裁剪区域
112
131
ctx . beginPath ( ) ;
113
132
ctx . rect ( x , y , width , height ) ;
114
133
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
+
120
181
ctx . restore ( ) ;
121
182
}
122
183
123
184
/**
124
185
* 渲染九宫格的四个角
125
186
*/
126
187
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 ,
138
199
bottom : number
139
200
) : void {
140
201
// 左上角
141
202
if ( left > 0 && top > 0 ) {
142
203
ctx . drawImage ( img , 0 , 0 , left , top , x , y , left , top ) ;
143
204
}
144
-
205
+
145
206
// 右上角
146
207
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 ) ;
149
210
}
150
-
211
+
151
212
// 左下角
152
213
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 ) ;
155
216
}
156
-
217
+
157
218
// 右下角
158
219
if ( right > 0 && bottom > 0 ) {
159
220
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 ) ;
161
222
}
162
223
}
163
224
164
225
/**
165
226
* 渲染九宫格的四条边
166
227
*/
167
228
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 ,
183
244
targetCenterHeight : number
184
245
) : void {
185
246
// 上边 - 水平拉伸
186
247
if ( top > 0 && targetCenterWidth > 0 ) {
187
248
ctx . drawImage ( img , left , 0 , centerSrcWidth , top ,
188
- x + left , y , targetCenterWidth , top ) ;
249
+ x + left , y , targetCenterWidth , top ) ;
189
250
}
190
-
251
+
191
252
// 下边 - 水平拉伸
192
253
if ( bottom > 0 && targetCenterWidth > 0 ) {
193
254
ctx . drawImage ( img , left , imgHeight - bottom , centerSrcWidth , bottom ,
194
- x + left , y + height - bottom , targetCenterWidth , bottom ) ;
255
+ x + left , y + height - bottom , targetCenterWidth , bottom ) ;
195
256
}
196
-
257
+
197
258
// 左边 - 垂直拉伸
198
259
if ( left > 0 && targetCenterHeight > 0 ) {
199
260
ctx . drawImage ( img , 0 , top , left , centerSrcHeight ,
200
- x , y + top , left , targetCenterHeight ) ;
261
+ x , y + top , left , targetCenterHeight ) ;
201
262
}
202
-
263
+
203
264
// 右边 - 垂直拉伸
204
265
if ( right > 0 && targetCenterHeight > 0 ) {
205
266
ctx . drawImage ( img , imgWidth - right , top , right , centerSrcHeight ,
206
- x + width - right , y + top , right , targetCenterHeight ) ;
267
+ x + width - right , y + top , right , targetCenterHeight ) ;
207
268
}
208
269
}
209
270
}
0 commit comments