image.go (22274B)
1 // Copyright 2014 Hajime Hoshi 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ebiten 16 17 import ( 18 "fmt" 19 "image" 20 "image/color" 21 22 "github.com/hajimehoshi/ebiten/v2/internal/driver" 23 "github.com/hajimehoshi/ebiten/v2/internal/graphics" 24 "github.com/hajimehoshi/ebiten/v2/internal/mipmap" 25 ) 26 27 // panicOnErrorAtImageAt indicates whether (*Image).At panics on an error or not. 28 // This value is set only on testing. 29 var panicOnErrorAtImageAt bool 30 31 // Image represents a rectangle set of pixels. 32 // The pixel format is alpha-premultiplied RGBA. 33 // Image implements image.Image and draw.Image. 34 type Image struct { 35 // addr holds self to check copying. 36 // See strings.Builder for similar examples. 37 addr *Image 38 39 mipmap *mipmap.Mipmap 40 41 bounds image.Rectangle 42 original *Image 43 } 44 45 func (i *Image) copyCheck() { 46 if i.addr != i { 47 panic("ebiten: illegal use of non-zero Image copied by value") 48 } 49 } 50 51 // Size returns the size of the image. 52 func (i *Image) Size() (width, height int) { 53 s := i.Bounds().Size() 54 return s.X, s.Y 55 } 56 57 func (i *Image) isDisposed() bool { 58 return i.mipmap == nil 59 } 60 61 func (i *Image) isSubImage() bool { 62 return i.original != nil 63 } 64 65 // Clear resets the pixels of the image into 0. 66 // 67 // When the image is disposed, Clear does nothing. 68 func (i *Image) Clear() { 69 i.Fill(color.Transparent) 70 } 71 72 // Fill fills the image with a solid color. 73 // 74 // When the image is disposed, Fill does nothing. 75 func (i *Image) Fill(clr color.Color) { 76 i.copyCheck() 77 78 if i.isDisposed() { 79 return 80 } 81 82 // TODO: Implement this. 83 if i.isSubImage() { 84 panic("ebiten: rendering to a sub-image is not implemented (Fill)") 85 } 86 87 i.mipmap.Fill(color.RGBAModel.Convert(clr).(color.RGBA)) 88 } 89 90 func canSkipMipmap(geom GeoM, filter driver.Filter) bool { 91 det := geom.det2x2() 92 if filter == driver.FilterNearest && det <= 1.001 { 93 return true 94 } 95 if filter == driver.FilterLinear && det >= 0.999 { 96 return true 97 } 98 return false 99 } 100 101 // DrawImageOptions represents options for DrawImage. 102 type DrawImageOptions struct { 103 // GeoM is a geometry matrix to draw. 104 // The default (zero) value is identity, which draws the image at (0, 0). 105 GeoM GeoM 106 107 // ColorM is a color matrix to draw. 108 // The default (zero) value is identity, which doesn't change any color. 109 ColorM ColorM 110 111 // CompositeMode is a composite mode to draw. 112 // The default (zero) value is regular alpha blending. 113 CompositeMode CompositeMode 114 115 // Filter is a type of texture filter. 116 // The default (zero) value is FilterNearest. 117 Filter Filter 118 } 119 120 // DrawImage draws the given image on the image i. 121 // 122 // DrawImage accepts the options. For details, see the document of 123 // DrawImageOptions. 124 // 125 // For drawing, the pixels of the argument image at the time of this call is 126 // adopted. Even if the argument image is mutated after this call, the drawing 127 // result is never affected. 128 // 129 // When the image i is disposed, DrawImage does nothing. 130 // When the given image img is disposed, DrawImage panics. 131 // 132 // When the given image is as same as i, DrawImage panics. 133 // 134 // DrawImage works more efficiently as batches 135 // when the successive calls of DrawImages satisfy the below conditions: 136 // 137 // * All render targets are same (A in A.DrawImage(B, op)) 138 // * Either all ColorM element values are same or all the ColorM have only 139 // diagonal ('scale') elements 140 // * If only (*ColorM).Scale is applied to a ColorM, the ColorM has only 141 // diagonal elements. The other ColorM functions might modify the other 142 // elements. 143 // * All CompositeMode values are same 144 // * All Filter values are same 145 // 146 // Even when all the above conditions are satisfied, multiple draw commands can 147 // be used in really rare cases. Ebiten images usually share an internal 148 // automatic texture atlas, but when you consume the atlas, or you create a huge 149 // image, those images cannot be on the same texture atlas. In this case, draw 150 // commands are separated. The texture atlas size is 4096x4096 so far. Another 151 // case is when you use an offscreen as a render source. An offscreen doesn't 152 // share the texture atlas with high probability. 153 // 154 // For more performance tips, see https://ebiten.org/documents/performancetips.html 155 func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { 156 i.copyCheck() 157 158 if img.isDisposed() { 159 panic("ebiten: the given image to DrawImage must not be disposed") 160 } 161 if i.isDisposed() { 162 return 163 } 164 165 // TODO: Implement this. 166 if i.isSubImage() { 167 panic("ebiten: render to a sub-image is not implemented (DrawImage)") 168 } 169 170 // Calculate vertices before locking because the user can do anything in 171 // options.ImageParts interface without deadlock (e.g. Call Image functions). 172 if options == nil { 173 options = &DrawImageOptions{} 174 } 175 176 bounds := img.Bounds() 177 mode := driver.CompositeMode(options.CompositeMode) 178 filter := driver.Filter(options.Filter) 179 180 a, b, c, d, tx, ty := options.GeoM.elements32() 181 182 sx0 := float32(bounds.Min.X) 183 sy0 := float32(bounds.Min.Y) 184 sx1 := float32(bounds.Max.X) 185 sy1 := float32(bounds.Max.Y) 186 vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, 1, 1, 1, 1, filter == driver.FilterScreen) 187 is := graphics.QuadIndices() 188 189 srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap} 190 i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, canSkipMipmap(options.GeoM, filter)) 191 } 192 193 // Vertex represents a vertex passed to DrawTriangles. 194 type Vertex struct { 195 // DstX and DstY represents a point on a destination image. 196 DstX float32 197 DstY float32 198 199 // SrcX and SrcY represents a point on a source image. 200 // Be careful that SrcX/SrcY coordinates are on the image's bounds. 201 // This means that a left-upper point of a sub-image might not be (0, 0). 202 SrcX float32 203 SrcY float32 204 205 // ColorR/ColorG/ColorB/ColorA represents color scaling values. 206 // 1 means the original source image color is used. 207 // 0 means a transparent color is used. 208 ColorR float32 209 ColorG float32 210 ColorB float32 211 ColorA float32 212 } 213 214 // Address represents a sampler address mode. 215 type Address int 216 217 const ( 218 // AddressUnsafe means there is no guarantee when the texture coodinates are out of range. 219 AddressUnsafe Address = Address(driver.AddressUnsafe) 220 221 // AddressClampToZero means that out-of-range texture coordinates return 0 (transparent). 222 AddressClampToZero Address = Address(driver.AddressClampToZero) 223 224 // AddressRepeat means that texture coordinates wrap to the other side of the texture. 225 AddressRepeat Address = Address(driver.AddressRepeat) 226 ) 227 228 // DrawTrianglesOptions represents options for DrawTriangles. 229 type DrawTrianglesOptions struct { 230 // ColorM is a color matrix to draw. 231 // The default (zero) value is identity, which doesn't change any color. 232 // ColorM is applied before vertex color scale is applied. 233 // 234 // If Shader is not nil, ColorM is ignored. 235 ColorM ColorM 236 237 // CompositeMode is a composite mode to draw. 238 // The default (zero) value is regular alpha blending. 239 CompositeMode CompositeMode 240 241 // Filter is a type of texture filter. 242 // The default (zero) value is FilterNearest. 243 Filter Filter 244 245 // Address is a sampler address mode. 246 // The default (zero) value is AddressUnsafe. 247 Address Address 248 } 249 250 // MaxIndicesNum is the maximum number of indices for DrawTriangles. 251 const MaxIndicesNum = graphics.IndicesNum 252 253 // DrawTriangles draws triangles with the specified vertices and their indices. 254 // 255 // If len(indices) is not multiple of 3, DrawTriangles panics. 256 // 257 // If len(indices) is more than MaxIndicesNum, DrawTriangles panics. 258 // 259 // The rule in which DrawTriangles works effectively is same as DrawImage's. 260 // 261 // When the given image is disposed, DrawTriangles panics. 262 // 263 // When the image i is disposed, DrawTriangles does nothing. 264 func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, options *DrawTrianglesOptions) { 265 i.copyCheck() 266 267 if img != nil && img.isDisposed() { 268 panic("ebiten: the given image to DrawTriangles must not be disposed") 269 } 270 if i.isDisposed() { 271 return 272 } 273 274 if i.isSubImage() { 275 panic("ebiten: render to a sub-image is not implemented (DrawTriangles)") 276 } 277 278 if len(indices)%3 != 0 { 279 panic("ebiten: len(indices) % 3 must be 0") 280 } 281 if len(indices) > MaxIndicesNum { 282 panic("ebiten: len(indices) must be <= MaxIndicesNum") 283 } 284 // TODO: Check the maximum value of indices and len(vertices)? 285 286 if options == nil { 287 options = &DrawTrianglesOptions{} 288 } 289 290 mode := driver.CompositeMode(options.CompositeMode) 291 292 address := driver.Address(options.Address) 293 var sr driver.Region 294 if address != driver.AddressUnsafe { 295 b := img.Bounds() 296 sr = driver.Region{ 297 X: float32(b.Min.X), 298 Y: float32(b.Min.Y), 299 Width: float32(b.Dx()), 300 Height: float32(b.Dy()), 301 } 302 } 303 304 filter := driver.Filter(options.Filter) 305 306 vs := make([]float32, len(vertices)*graphics.VertexFloatNum) 307 for i, v := range vertices { 308 vs[i*graphics.VertexFloatNum] = v.DstX 309 vs[i*graphics.VertexFloatNum+1] = v.DstY 310 vs[i*graphics.VertexFloatNum+2] = v.SrcX 311 vs[i*graphics.VertexFloatNum+3] = v.SrcY 312 vs[i*graphics.VertexFloatNum+4] = v.ColorR 313 vs[i*graphics.VertexFloatNum+5] = v.ColorG 314 vs[i*graphics.VertexFloatNum+6] = v.ColorB 315 vs[i*graphics.VertexFloatNum+7] = v.ColorA 316 } 317 is := make([]uint16, len(indices)) 318 copy(is, indices) 319 320 srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap} 321 322 i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, address, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) 323 } 324 325 // DrawTrianglesShaderOptions represents options for DrawTrianglesShader. 326 // 327 // This API is experimental. 328 type DrawTrianglesShaderOptions struct { 329 // CompositeMode is a composite mode to draw. 330 // The default (zero) value is regular alpha blending. 331 CompositeMode CompositeMode 332 333 // Uniforms is a set of uniform variables for the shader. 334 // The keys are the names of the uniform variables. 335 // The values must be float or []float. 336 // If the uniform variable type is an array, a vector or a matrix, 337 // you have to specify linearly flattened values as a slice. 338 // For example, if the uniform variable type is [4]vec4, the number of the slice values will be 16. 339 Uniforms map[string]interface{} 340 341 // Images is a set of the source images. 342 // All the image must be the same size. 343 Images [4]*Image 344 } 345 346 func init() { 347 var op DrawTrianglesShaderOptions 348 if got, want := len(op.Images), graphics.ShaderImageNum; got != want { 349 panic(fmt.Sprintf("ebiten: len((DrawTrianglesShaderOptions{}).Images) must be %d but %d", want, got)) 350 } 351 } 352 353 // DrawTrianglesShader draws triangles with the specified vertices and their indices with the specified shader. 354 // 355 // For the details about the shader, see https://ebiten.org/documents/shader.html. 356 // 357 // If len(indices) is not multiple of 3, DrawTrianglesShader panics. 358 // 359 // If len(indices) is more than MaxIndicesNum, DrawTrianglesShader panics. 360 // 361 // When a specified image is non-nil and is disposed, DrawTrianglesShader panics. 362 // 363 // When the image i is disposed, DrawTrianglesShader does nothing. 364 // 365 // This API is experimental. 366 func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesShaderOptions) { 367 i.copyCheck() 368 369 if i.isDisposed() { 370 return 371 } 372 373 if i.isSubImage() { 374 panic("ebiten: render to a sub-image is not implemented (DrawTrianglesShader)") 375 } 376 377 if len(indices)%3 != 0 { 378 panic("ebiten: len(indices) % 3 must be 0") 379 } 380 if len(indices) > MaxIndicesNum { 381 panic("ebiten: len(indices) must be <= MaxIndicesNum") 382 } 383 // TODO: Check the maximum value of indices and len(vertices)? 384 385 if options == nil { 386 options = &DrawTrianglesShaderOptions{} 387 } 388 389 mode := driver.CompositeMode(options.CompositeMode) 390 391 vs := make([]float32, len(vertices)*graphics.VertexFloatNum) 392 for i, v := range vertices { 393 vs[i*graphics.VertexFloatNum] = v.DstX 394 vs[i*graphics.VertexFloatNum+1] = v.DstY 395 vs[i*graphics.VertexFloatNum+2] = v.SrcX 396 vs[i*graphics.VertexFloatNum+3] = v.SrcY 397 vs[i*graphics.VertexFloatNum+4] = v.ColorR 398 vs[i*graphics.VertexFloatNum+5] = v.ColorG 399 vs[i*graphics.VertexFloatNum+6] = v.ColorB 400 vs[i*graphics.VertexFloatNum+7] = v.ColorA 401 } 402 is := make([]uint16, len(indices)) 403 copy(is, indices) 404 405 var imgs [graphics.ShaderImageNum]*mipmap.Mipmap 406 var imgw, imgh int 407 for i, img := range options.Images { 408 if img == nil { 409 continue 410 } 411 if img.isDisposed() { 412 panic("ebiten: the given image to DrawRectShader must not be disposed") 413 } 414 if i == 0 { 415 imgw, imgh = img.Size() 416 } else { 417 // TODO: Check imgw > 0 && imgh > 0 418 if w, h := img.Size(); imgw != w || imgh != h { 419 panic("ebiten: all the source images must be the same size with the rectangle") 420 } 421 } 422 imgs[i] = img.mipmap 423 } 424 425 var sx, sy float32 426 if options.Images[0] != nil { 427 b := options.Images[0].Bounds() 428 sx = float32(b.Min.X) 429 sy = float32(b.Min.Y) 430 } 431 432 var sr driver.Region 433 if img := options.Images[0]; img != nil { 434 b := img.Bounds() 435 sr = driver.Region{ 436 X: float32(b.Min.X), 437 Y: float32(b.Min.Y), 438 Width: float32(b.Dx()), 439 Height: float32(b.Dy()), 440 } 441 } 442 443 var offsets [graphics.ShaderImageNum - 1][2]float32 444 for i, img := range options.Images[1:] { 445 if img == nil { 446 continue 447 } 448 b := img.Bounds() 449 offsets[i][0] = -sx + float32(b.Min.X) 450 offsets[i][1] = -sy + float32(b.Min.Y) 451 } 452 453 us := shader.convertUniforms(options.Uniforms) 454 i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, sr, offsets, shader.shader, us, false) 455 } 456 457 // DrawRectShaderOptions represents options for DrawRectShader. 458 // 459 // This API is experimental. 460 type DrawRectShaderOptions struct { 461 // GeoM is a geometry matrix to draw. 462 // The default (zero) value is identity, which draws the rectangle at (0, 0). 463 GeoM GeoM 464 465 // CompositeMode is a composite mode to draw. 466 // The default (zero) value is regular alpha blending. 467 CompositeMode CompositeMode 468 469 // Uniforms is a set of uniform variables for the shader. 470 // The keys are the names of the uniform variables. 471 // The values must be float or []float. 472 // If the uniform variable type is an array, a vector or a matrix, 473 // you have to specify linearly flattened values as a slice. 474 // For example, if the uniform variable type is [4]vec4, the number of the slice values will be 16. 475 Uniforms map[string]interface{} 476 477 // Images is a set of the source images. 478 // All the image must be the same size with the rectangle. 479 Images [4]*Image 480 } 481 482 func init() { 483 var op DrawRectShaderOptions 484 if got, want := len(op.Images), graphics.ShaderImageNum; got != want { 485 panic(fmt.Sprintf("ebiten: len((DrawRectShaderOptions{}).Images) must be %d but %d", want, got)) 486 } 487 } 488 489 // DrawRectShader draws a rectangle with the specified width and height with the specified shader. 490 // 491 // For the details about the shader, see https://ebiten.org/documents/shader.html. 492 // 493 // When one of the specified image is non-nil and is disposed, DrawRectShader panics. 494 // 495 // When the image i is disposed, DrawRectShader does nothing. 496 // 497 // This API is experimental. 498 func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawRectShaderOptions) { 499 i.copyCheck() 500 501 if i.isDisposed() { 502 return 503 } 504 505 // TODO: Implement this. 506 if i.isSubImage() { 507 panic("ebiten: rendering to a sub-image is not implemented (DrawRectShader)") 508 } 509 510 if options == nil { 511 options = &DrawRectShaderOptions{} 512 } 513 514 mode := driver.CompositeMode(options.CompositeMode) 515 516 var imgs [graphics.ShaderImageNum]*mipmap.Mipmap 517 for i, img := range options.Images { 518 if img == nil { 519 continue 520 } 521 if img.isDisposed() { 522 panic("ebiten: the given image to DrawRectShader must not be disposed") 523 } 524 if w, h := img.Size(); width != w || height != h { 525 panic("ebiten: all the source images must be the same size with the rectangle") 526 } 527 imgs[i] = img.mipmap 528 } 529 530 var sx, sy float32 531 if options.Images[0] != nil { 532 b := options.Images[0].Bounds() 533 sx = float32(b.Min.X) 534 sy = float32(b.Min.Y) 535 } 536 537 a, b, c, d, tx, ty := options.GeoM.elements32() 538 vs := graphics.QuadVertices(sx, sy, sx+float32(width), sy+float32(height), a, b, c, d, tx, ty, 1, 1, 1, 1, false) 539 is := graphics.QuadIndices() 540 541 var sr driver.Region 542 if img := options.Images[0]; img != nil { 543 b := img.Bounds() 544 sr = driver.Region{ 545 X: float32(b.Min.X), 546 Y: float32(b.Min.Y), 547 Width: float32(b.Dx()), 548 Height: float32(b.Dy()), 549 } 550 } 551 552 var offsets [graphics.ShaderImageNum - 1][2]float32 553 for i, img := range options.Images[1:] { 554 if img == nil { 555 continue 556 } 557 b := img.Bounds() 558 offsets[i][0] = -sx + float32(b.Min.X) 559 offsets[i][1] = -sy + float32(b.Min.Y) 560 } 561 562 us := shader.convertUniforms(options.Uniforms) 563 i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, sr, offsets, shader.shader, us, canSkipMipmap(options.GeoM, driver.FilterNearest)) 564 } 565 566 // SubImage returns an image representing the portion of the image p visible through r. 567 // The returned value shares pixels with the original image. 568 // 569 // The returned value is always *ebiten.Image. 570 // 571 // If the image is disposed, SubImage returns nil. 572 // 573 // In the current Ebiten implementation, SubImage is available only as a rendering source. 574 func (i *Image) SubImage(r image.Rectangle) image.Image { 575 i.copyCheck() 576 if i.isDisposed() { 577 return nil 578 } 579 580 r = r.Intersect(i.Bounds()) 581 // Need to check Empty explicitly. See the standard image package implementations. 582 if r.Empty() { 583 r = image.ZR 584 } 585 586 // Keep the original image's reference not to dispose that by GC. 587 var orig = i 588 if i.isSubImage() { 589 orig = i.original 590 } 591 592 img := &Image{ 593 mipmap: i.mipmap, 594 bounds: r, 595 original: orig, 596 } 597 img.addr = img 598 599 return img 600 } 601 602 // Bounds returns the bounds of the image. 603 func (i *Image) Bounds() image.Rectangle { 604 if i.isDisposed() { 605 panic("ebiten: the image is already disposed") 606 } 607 return i.bounds 608 } 609 610 // ColorModel returns the color model of the image. 611 func (i *Image) ColorModel() color.Model { 612 return color.RGBAModel 613 } 614 615 // At returns the color of the image at (x, y). 616 // 617 // At loads pixels from GPU to system memory if necessary, which means that At can be slow. 618 // 619 // At always returns a transparent color if the image is disposed. 620 // 621 // Note that an important logic should not rely on values returned by At, since 622 // the returned values can include very slight differences between some machines. 623 // 624 // At can't be called outside the main loop (ebiten.Run's updating function) starts. 625 func (i *Image) At(x, y int) color.Color { 626 if i.isDisposed() { 627 return color.RGBA{} 628 } 629 if !image.Pt(x, y).In(i.Bounds()) { 630 return color.RGBA{} 631 } 632 pix, err := i.mipmap.Pixels(x, y, 1, 1) 633 if err != nil { 634 if panicOnErrorAtImageAt { 635 panic(err) 636 } 637 theUIContext.setError(err) 638 return color.RGBA{} 639 } 640 return color.RGBA{pix[0], pix[1], pix[2], pix[3]} 641 } 642 643 // Set sets the color at (x, y). 644 // 645 // Set loads pixels from GPU to system memory if necessary, which means that Set can be slow. 646 // 647 // In the current implementation, successive calls of Set invokes loading pixels at most once, so this is efficient. 648 // 649 // If the image is disposed, Set does nothing. 650 func (i *Image) Set(x, y int, clr color.Color) { 651 i.copyCheck() 652 if i.isDisposed() { 653 return 654 } 655 if !image.Pt(x, y).In(i.Bounds()) { 656 return 657 } 658 if i.isSubImage() { 659 i = i.original 660 } 661 662 r, g, b, a := clr.RGBA() 663 pix := []byte{byte(r >> 8), byte(g >> 8), byte(b >> 8), byte(a >> 8)} 664 if err := i.mipmap.ReplacePixels(pix, x, y, 1, 1); err != nil { 665 theUIContext.setError(err) 666 } 667 } 668 669 // Dispose disposes the image data. 670 // After disposing, most of image functions do nothing and returns meaningless values. 671 // 672 // Calling Dispose is not mandatory. GC automatically collects internal resources that no objects refer to. 673 // However, calling Dispose explicitly is helpful if memory usage matters. 674 // 675 // When the image is disposed, Dipose does nothing. 676 func (i *Image) Dispose() { 677 i.copyCheck() 678 679 if i.isDisposed() { 680 return 681 } 682 if i.isSubImage() { 683 return 684 } 685 i.mipmap.MarkDisposed() 686 i.mipmap = nil 687 } 688 689 // ReplacePixels replaces the pixels of the image with p. 690 // 691 // The given p must represent RGBA pre-multiplied alpha values. 692 // len(pix) must equal to 4 * (bounds width) * (bounds height). 693 // 694 // ReplacePixels works on a sub-image. 695 // 696 // When len(pix) is not appropriate, ReplacePixels panics. 697 // 698 // When the image is disposed, ReplacePixels does nothing. 699 func (i *Image) ReplacePixels(pixels []byte) { 700 i.copyCheck() 701 702 if i.isDisposed() { 703 return 704 } 705 r := i.Bounds() 706 707 // Do not need to copy pixels here. 708 // * In internal/mipmap, pixels are copied when necessary. 709 // * In internal/shareable, pixels are copied to make its paddings. 710 if err := i.mipmap.ReplacePixels(pixels, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil { 711 theUIContext.setError(err) 712 } 713 } 714 715 // NewImage returns an empty image. 716 // 717 // If width or height is less than 1 or more than device-dependent maximum size, NewImage panics. 718 func NewImage(width, height int) *Image { 719 i := &Image{ 720 mipmap: mipmap.New(width, height), 721 bounds: image.Rect(0, 0, width, height), 722 } 723 i.addr = i 724 return i 725 } 726 727 // NewImageFromImage creates a new image with the given image (source). 728 // 729 // If source's width or height is less than 1 or more than device-dependent maximum size, NewImageFromImage panics. 730 func NewImageFromImage(source image.Image) *Image { 731 size := source.Bounds().Size() 732 733 width, height := size.X, size.Y 734 735 i := &Image{ 736 mipmap: mipmap.New(width, height), 737 bounds: image.Rect(0, 0, width, height), 738 } 739 i.addr = i 740 741 i.ReplacePixels(imageToBytes(source)) 742 return i 743 } 744 745 func newScreenFramebufferImage(width, height int) *Image { 746 i := &Image{ 747 mipmap: mipmap.NewScreenFramebufferMipmap(width, height), 748 bounds: image.Rect(0, 0, width, height), 749 } 750 i.addr = i 751 return i 752 }