├── example ├── source.jpg └── sample.go ├── README ├── gdcompat.go └── gd.go /example/source.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bolknote/go-gd/HEAD/example/source.jpg -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Go bingings for GD 2 | http://bolknote.ru 3 | Evgeny Stepanischev 4 | imbolk@gmail.com 5 | 6 | Installation: 7 | 1. install libgd (https://libgd.github.io/) 8 | 2. just run "go get github.com/bolknote/go-gd" 9 | 10 | For test you can run sample.go (go run sample.jpg) -------------------------------------------------------------------------------- /example/sample.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import gd "github.com/bolknote/go-gd" 4 | import "fmt" 5 | 6 | func main() { 7 | // http://www.php.net/manual/en/function.imagecreatefromjpeg.php 8 | pict := gd.CreateFromJpeg("source.jpg") 9 | 10 | // http://www.php.net/manual/en/function.imagedestroy.php 11 | defer pict.Destroy() 12 | 13 | pict.Sharpen(10) 14 | pict.Brightness(50) 15 | 16 | // http://www.php.net/manual/en/function.imagecolorallocate.php 17 | black := pict.ColorAllocate(0, 0, 0) 18 | white := pict.ColorAllocate(255, 255, 255) 19 | 20 | // http://php.net/manual/en/function.imagefilledpolygon.php 21 | pict.FilledPolygon([]gd.Point{{200, 200}, {210, 210}, {212, 250}}, black) 22 | 23 | pict.SmoothFilledEllipse(20, 20, 32, 32, white) 24 | 25 | // http://www.php.net/manual/en/function.imagefilledellipse.php 26 | pict.FilledEllipse(100, 100, 40, 50, white) 27 | 28 | // http://www.php.net/manual/en/function.imagecopyresampled.php 29 | pict.CopyResampled(pict, 40, 40, pict.Sx()-41, pict.Sy()-41, 20, 20, 40, 40) 30 | 31 | // Non-Unicode font 32 | font := gd.GetFont(gd.FONTGIANT) 33 | 34 | // http://www.php.net/manual/en/function.imagechar.php 35 | pict.Char(font, 100, 100, "B", black) 36 | // http://www.php.net/manual/en/function.imagestring.php 37 | pict.String(font, 100, 120, "bolknote.ru", black) 38 | 39 | // Unicode font 40 | fonts := gd.GetFonts() 41 | fmt.Printf("Found %d X11 TTF font(s)\n", len(fonts)) 42 | 43 | if l := len(fonts); l > 0 { 44 | pict.StringFT(black, fonts[l-1], 12, 0, 100, 150, "Hello! Привет!") 45 | } 46 | 47 | // http://www.php.net/Imagejpeg 48 | pict.Jpeg("out.jpg", 95) 49 | } 50 | -------------------------------------------------------------------------------- /gdcompat.go: -------------------------------------------------------------------------------- 1 | package gd 2 | 3 | import "path/filepath" 4 | import "strings" 5 | import "io/ioutil" 6 | import . "math" 7 | import "runtime" 8 | 9 | func abs(i int) int { 10 | if i < 0 { 11 | return -i 12 | } 13 | 14 | return i 15 | } 16 | 17 | // Stack Blur Algorithm by Mario Klingemann 18 | // "Go" language port by Evgeny Stepanischev http://bolknote.ru 19 | func (img *Image) StackBlur(radius int, keepalpha bool) { 20 | if radius < 1 { 21 | return 22 | } 23 | 24 | w, h := int(img.Sx()), int(img.Sy()) 25 | wm, hm, wh, div := w-1, h-1, w*h, radius*2+1 26 | 27 | len := map[bool]int{true: 3, false: 4}[keepalpha] 28 | 29 | rgba := make([][]byte, len) 30 | for i := 0; i < len; i++ { 31 | rgba[i] = make([]byte, wh) 32 | } 33 | 34 | vmin := make([]int, max(w, h)) 35 | 36 | var x, y, i, yp, yi, yw, stackpointer, stackstart, rbs int 37 | var sir *[4]byte 38 | 39 | divsum := (div + 1) >> 1 40 | divsum *= divsum 41 | 42 | dv := make([]byte, 256*divsum) 43 | 44 | for i = 0; i < 256*divsum; i++ { 45 | dv[i] = byte(i / divsum) 46 | } 47 | 48 | yw, yi = 0, 0 49 | stack := make([][4]byte, div) 50 | r1 := radius + 1 51 | 52 | for y = 0; y < h; y++ { 53 | sum := make([]int, len) 54 | insum := make([]int, len) 55 | outsum := make([]int, len) 56 | 57 | for i = -radius; i <= radius; i++ { 58 | coords := yi + min(wm, max(i, 0)) 59 | yc := coords / w 60 | xc := coords % w 61 | 62 | p := img.ColorsForIndex(img.ColorAt(xc, yc)) 63 | 64 | sir = &stack[i+radius] 65 | sir[0] = (byte)(p["red"]) 66 | sir[1] = (byte)(p["green"]) 67 | sir[2] = (byte)(p["blue"]) 68 | sir[3] = (byte)(p["alpha"]) 69 | 70 | rbs = r1 - abs(i) 71 | for i := 0; i < len; i++ { 72 | sum[i] += int(sir[i]) * rbs 73 | } 74 | 75 | if i > 0 { 76 | for i := 0; i < len; i++ { 77 | insum[i] += int(sir[i]) 78 | } 79 | } else { 80 | for i := 0; i < len; i++ { 81 | outsum[i] += int(sir[i]) 82 | } 83 | } 84 | } 85 | 86 | stackpointer = radius 87 | 88 | for x = 0; x < w; x++ { 89 | for i := 0; i < len; i++ { 90 | rgba[i][yi] = dv[sum[i]] 91 | sum[i] -= outsum[i] 92 | } 93 | 94 | stackstart = stackpointer - radius + div 95 | sir = &stack[stackstart%div] 96 | 97 | for i := 0; i < len; i++ { 98 | outsum[i] -= int(sir[i]) 99 | } 100 | 101 | if y == 0 { 102 | vmin[x] = min(x+radius+1, wm) 103 | } 104 | 105 | coords := yw + vmin[x] 106 | yc := coords / w 107 | xc := coords % w 108 | 109 | p := img.ColorsForIndex(img.ColorAt(xc, yc)) 110 | 111 | sir[0] = byte(p["red"]) 112 | sir[1] = byte(p["green"]) 113 | sir[2] = byte(p["blue"]) 114 | sir[3] = byte(p["alpha"]) 115 | 116 | for i := 0; i < len; i++ { 117 | insum[i] += int(sir[i]) 118 | sum[i] += insum[i] 119 | } 120 | 121 | stackpointer = (stackpointer + 1) % div 122 | sir = &stack[stackpointer%div] 123 | 124 | for i := 0; i < len; i++ { 125 | outsum[i] += int(sir[i]) 126 | insum[i] -= int(sir[i]) 127 | } 128 | 129 | yi++ 130 | } 131 | 132 | yw += w 133 | } 134 | 135 | for x = 0; x < w; x++ { 136 | sum := make([]int, len) 137 | insum := make([]int, len) 138 | outsum := make([]int, len) 139 | 140 | yp = -radius * w 141 | 142 | for i = -radius; i <= radius; i++ { 143 | yi = max(0, yp) + x 144 | 145 | sir = &stack[i+radius] 146 | 147 | for i := 0; i < len; i++ { 148 | sir[i] = rgba[i][yi] 149 | } 150 | rbs = r1 - abs(i) 151 | 152 | for i := 0; i < len; i++ { 153 | sum[i] += int(rgba[i][yi]) * rbs 154 | } 155 | 156 | if i > 0 { 157 | for i := 0; i < len; i++ { 158 | insum[i] += int(sir[i]) 159 | } 160 | } else { 161 | for i := 0; i < len; i++ { 162 | outsum[i] += int(sir[i]) 163 | } 164 | } 165 | 166 | if i < hm { 167 | yp += w 168 | } 169 | } 170 | 171 | yi = x 172 | 173 | stackpointer = radius 174 | 175 | for y = 0; y < h; y++ { 176 | var alpha int 177 | 178 | if keepalpha { 179 | alpha = img.ColorsForIndex(img.ColorAt(yi%w, yi/w))["alpha"] 180 | } else { 181 | alpha = int(dv[sum[3]]) 182 | } 183 | 184 | newpxl := img.ColorAllocateAlpha(int(dv[sum[0]]), int(dv[sum[1]]), int(dv[sum[2]]), alpha) 185 | if newpxl == -1 { 186 | newpxl = img.ColorClosestAlpha(int(dv[sum[0]]), int(dv[sum[1]]), int(dv[sum[2]]), alpha) 187 | } 188 | 189 | img.SetPixel(yi%w, yi/w, newpxl) 190 | 191 | for i := 0; i < len; i++ { 192 | sum[i] -= outsum[i] 193 | } 194 | 195 | stackstart = stackpointer - radius + div 196 | sir = &stack[stackstart%div] 197 | 198 | for i := 0; i < len; i++ { 199 | outsum[i] -= int(sir[i]) 200 | } 201 | 202 | if x == 0 { 203 | vmin[y] = min(y+r1, hm) * w 204 | } 205 | 206 | p := x + vmin[y] 207 | 208 | for i := 0; i < len; i++ { 209 | sir[i] = rgba[i][p] 210 | insum[i] += int(sir[i]) 211 | sum[i] += insum[i] 212 | } 213 | 214 | stackpointer = (stackpointer + 1) % div 215 | sir = &stack[stackpointer] 216 | 217 | for i := 0; i < len; i++ { 218 | outsum[i] += int(sir[i]) 219 | insum[i] -= int(sir[i]) 220 | } 221 | 222 | yi += w 223 | } 224 | } 225 | } 226 | 227 | // Originally written from scratch by Ulrich Mierendorff, 06/2006 228 | // Rewritten and improved 04/2007, 07/2007, optimized circle version 03/2008 229 | // "Go" language port by Evgeny Stepanischev http://bolknote.ru 230 | 231 | func smootharcsegment(p *Image, cx, cy, a, b, aaAngleX, aaAngleY float64, fillColor Color, start, stop, seg float64) { 232 | xStart := Abs(a * Cos(start)) 233 | yStart := Abs(b * Sin(start)) 234 | xStop := Abs(a * Cos(stop)) 235 | yStop := Abs(b * Sin(stop)) 236 | 237 | dxStart, dyStart, dxStop, dyStop := float64(0), float64(0), float64(0), float64(0) 238 | 239 | color := p.ColorsForIndex(fillColor) 240 | 241 | if xStart != 0 { 242 | dyStart = yStart / xStart 243 | } 244 | 245 | if xStop != 0 { 246 | dyStop = yStop / xStop 247 | } 248 | 249 | if yStart != 0 { 250 | dxStart = xStart / yStart 251 | } 252 | 253 | if yStop != 0 { 254 | dxStop = xStop / yStop 255 | } 256 | 257 | aaStartX := Abs(xStart) >= Abs(yStart) 258 | aaStopX := xStop >= yStop 259 | 260 | for x := float64(0); x < a; x++ { 261 | _y1 := dyStop * x 262 | _y2 := dyStart * x 263 | 264 | var error1, error2 float64 265 | 266 | if xStart > xStop { 267 | error1 = _y1 - float64(int(_y1)) 268 | error2 = 1 - _y2 + float64(int(_y2)) 269 | 270 | _y1 -= error1 271 | _y2 += error2 272 | } else { 273 | error1 = 1 - _y1 + float64(int(_y1)) 274 | error2 = _y2 - float64(int(_y2)) 275 | 276 | _y1 += error1 277 | _y2 -= error2 278 | } 279 | 280 | switch seg { 281 | case 0: 282 | fallthrough 283 | case 2: 284 | var y1, y2 float64 285 | i := seg 286 | 287 | if !(start > i*Pi/2 && x > xStart) { 288 | var xp, yp, xa, ya float64 289 | 290 | if i == 0 { 291 | xp, yp, xa, ya = 1, -1, 1, 0 292 | } else { 293 | xp, yp, xa, ya = -1, 1, 0, 1 294 | } 295 | 296 | if stop < (i+1)*Pi/2 && x <= xStop { 297 | alpha := int(127 - float64(127-color["alpha"])*error1) 298 | diffColor1 := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 299 | 300 | y1 = _y1 301 | 302 | if aaStopX { 303 | xx := int(cx + xp*x + xa) 304 | yy := int(cy + yp*(y1+1) + ya) 305 | 306 | p.SetPixel(xx, yy, diffColor1) 307 | } 308 | } else { 309 | y := b * Sqrt(1-Pow(x, 2)/Pow(a, 2)) 310 | error := y - float64(int(y)) 311 | y = float64(int(y)) 312 | 313 | alpha := int(127 - float64(127-color["alpha"])*error) 314 | diffColor := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 315 | 316 | y1 = y 317 | if x < aaAngleX { 318 | xx := int(cx + xp*x + xa) 319 | yy := int(cy + yp*(y1+1) + ya) 320 | 321 | p.SetPixel(xx, yy, diffColor) 322 | } 323 | } 324 | 325 | if start > i*Pi/2 && x <= xStart { 326 | alpha := int(127 - float64(127-color["alpha"])*error2) 327 | diffColor2 := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 328 | 329 | y2 = _y2 330 | if aaStartX { 331 | xx := int(cx + xp*x + xa) 332 | yy := int(cy + yp*(y2-1) + ya) 333 | 334 | p.SetPixel(xx, yy, diffColor2) 335 | } 336 | } else { 337 | y2 = 0 338 | } 339 | 340 | if y2 <= y1 { 341 | xx := int(cx + xp*x + xa) 342 | yy1 := int(cy + yp*y1 + ya) 343 | yy2 := int(cy + yp*y2 + ya) 344 | 345 | p.Line(xx, yy1, xx, yy2, fillColor) 346 | } 347 | } 348 | 349 | case 1: 350 | fallthrough 351 | case 3: 352 | var y1, y2 float64 353 | i := seg 354 | 355 | if !(stop < (i+1)*Pi/2 && x > xStop) { 356 | var xp, yp, xa, ya float64 357 | 358 | if i == 1 { 359 | xp, yp, xa, ya = -1, -1, 0, 0 360 | } else { 361 | xp, yp, xa, ya = 1, 1, 1, 1 362 | } 363 | 364 | if start > i*Pi/2 && x < xStart { 365 | alpha := int(127 - float64(127-color["alpha"])*error2) 366 | diffColor2 := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 367 | 368 | y1 = _y2 369 | if aaStartX { 370 | xx := int(cx + xp*x + xa) 371 | yy := int(cy + yp*(y1+1) + ya) 372 | 373 | p.SetPixel(xx, yy, diffColor2) 374 | } 375 | } else { 376 | y := b * Sqrt(1-Pow(x, 2)/Pow(a, 2)) 377 | error := y - float64(int(y)) 378 | y = float64(int(y)) 379 | 380 | alpha := int(127 - float64(127-color["alpha"])*error) 381 | diffColor := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 382 | 383 | y1 = y 384 | if x < aaAngleX { 385 | xx := int(cx + xp*x + xa) 386 | yy := int(cy + yp*(y1+1) + ya) 387 | 388 | p.SetPixel(xx, yy, diffColor) 389 | } 390 | } 391 | 392 | if stop < (i+1)*Pi/2 && x <= xStop { 393 | alpha := int(127 - float64(127-color["alpha"])*error1) 394 | diffColor1 := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 395 | 396 | y2 = _y1 397 | if aaStopX { 398 | xx := int(cx + xp*x + xa) 399 | yy := int(cy + yp*(y2-1) + ya) 400 | p.SetPixel(xx, yy, diffColor1) 401 | } 402 | } else { 403 | y2 = 0 404 | } 405 | 406 | if y2 <= y1 { 407 | xx := int(cx + xp*x + xa) 408 | yy1 := int(cy + yp*y1 + ya) 409 | yy2 := int(cy + yp*y2 + ya) 410 | 411 | p.Line(xx, yy1, xx, yy2, fillColor) 412 | } 413 | } 414 | } // switch 415 | } // for x 416 | 417 | for y := float64(0); y < float64(b); y++ { 418 | _x1 := dxStop * y 419 | _x2 := dxStart * y 420 | 421 | var error1, error2 float64 422 | 423 | if yStart > yStop { 424 | error1 = _x1 - float64(int(_x1)) 425 | error2 = 1 - _x2 - float64(int(_x2)) 426 | _x1 -= error1 427 | _x2 += error2 428 | } else { 429 | error1 = 1 - _x1 + float64(int(_x1)) 430 | error2 = _x2 + float64(int(_x2)) 431 | _x1 += error1 432 | _x2 -= error2 433 | } 434 | 435 | switch seg { 436 | case 0: 437 | fallthrough 438 | case 2: 439 | var x1, x2 float64 440 | i := seg 441 | 442 | if !(start > i*Pi/2 && y > yStop) { 443 | var xp, yp, xa, ya float64 444 | 445 | if i == 0 { 446 | xp, yp, xa, ya = 1, -1, 1, 0 447 | } else { 448 | xp, yp, xa, ya = -1, 1, 0, 1 449 | } 450 | 451 | if stop < (i+1)*Pi/2 && y <= yStop { 452 | alpha := int(127 - float64(127-color["alpha"])*error1) 453 | diffColor1 := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 454 | 455 | x1 = _x1 456 | if !aaStopX { 457 | xx := int(cx + xp*(x1-1) + xa) 458 | yy := int(cy + yp*y + ya) 459 | 460 | p.SetPixel(xx, yy, diffColor1) 461 | } 462 | } 463 | 464 | if start > i*Pi/2 && y < yStart { 465 | alpha := int(127 - float64(127-color["alpha"])*error2) 466 | diffColor2 := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 467 | 468 | x2 = _x2 469 | if !aaStartX { 470 | xx := int(cx + xp*(x2+1) + xa) 471 | yy := int(cy + yp*y + ya) 472 | 473 | p.SetPixel(xx, yy, diffColor2) 474 | } 475 | } else { 476 | x := a * Sqrt(1-Pow(y, 2)/Pow(b, 2)) 477 | error := x - float64(int(x)) 478 | x = float64(int(x)) 479 | 480 | alpha := int(127 - float64(127-color["alpha"])*error) 481 | diffColor := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 482 | 483 | x1 = x 484 | if y < aaAngleY && y <= yStop { 485 | xx := int(cx + xp*(x1+1) + xa) 486 | yy := int(cy + yp*y + ya) 487 | 488 | p.SetPixel(xx, yy, diffColor) 489 | } 490 | } 491 | } 492 | 493 | case 1: 494 | fallthrough 495 | case 3: 496 | var x1, x2 float64 497 | i := seg 498 | 499 | if !(stop < (i+1)*Pi/2 && y > yStart) { 500 | var xp, yp, xa, ya float64 501 | 502 | if i == 1 { 503 | xp, yp, xa, ya = -1, -1, 0, 0 504 | } else { 505 | xp, yp, xa, ya = 1, 1, 1, 1 506 | } 507 | 508 | if start > i*Pi/2 && y < yStart { 509 | alpha := int(127 - float64(127-color["alpha"])*error2) 510 | diffColor2 := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 511 | 512 | x1 = _x2 513 | if !aaStartX { 514 | xx := int(cx + xp*(x1-1) + xa) 515 | yy := int(cy + yp*y + ya) 516 | 517 | p.SetPixel(xx, yy, diffColor2) 518 | } 519 | } 520 | 521 | if stop < (i+1)*Pi/2 && y <= yStop { 522 | alpha := int(127 - float64(127-color["alpha"])*error1) 523 | diffColor1 := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 524 | 525 | x2 = _x1 526 | if !aaStopX { 527 | xx := int(cx + xp*(x2+1) + xa) 528 | yy := int(cy + yp*y + ya) 529 | 530 | p.SetPixel(xx, yy, diffColor1) 531 | } 532 | } else { 533 | x := a * Sqrt(1-Pow(y, 2)/Pow(b, 2)) 534 | error := x - float64(int(x)) 535 | x = float64(int(x)) 536 | 537 | alpha := int(127 - float64(127-color["alpha"])*error) 538 | diffColor := p.ColorExactAlpha(color["red"], color["green"], color["blue"], alpha) 539 | 540 | x1 = x 541 | if y < aaAngleY && y < yStart { 542 | xx := int(cx + xp*(x1+1) + xa) 543 | yy := int(cy + yp*y + ya) 544 | 545 | p.SetPixel(xx, yy, diffColor) 546 | } 547 | } 548 | } 549 | } // switch 550 | } // for y 551 | } 552 | 553 | func round(f float64) float64 { 554 | if f-float64(int(f)) >= 0.5 { 555 | return Ceil(f) 556 | } 557 | 558 | return Floor(f) 559 | } 560 | 561 | // Parameters: 562 | // cx - Center of ellipse, X-coord 563 | // cy - Center of ellipse, Y-coord 564 | // w - Width of ellipse ($w >= 2) 565 | // h - Height of ellipse ($h >= 2 ) 566 | // color - Color of ellipse as a four component array with RGBA 567 | // start - Starting angle of the arc, no limited range! 568 | // stop - Stop angle of the arc, no limited range! 569 | // start _can_ be greater than $stop! 570 | 571 | func (p *Image) SmoothFilledArc(cx, cy, w, h int, color Color, start, stop float64) { 572 | for start < 0 { 573 | start += 2 * Pi 574 | } 575 | 576 | for stop < 0 { 577 | stop += 2 * Pi 578 | } 579 | 580 | for start > 2*Pi { 581 | start -= 2 * Pi 582 | } 583 | 584 | for stop > 2*Pi { 585 | stop -= 2 * Pi 586 | } 587 | 588 | if start > stop { 589 | p.SmoothFilledArc(cx, cy, w, h, color, start, 2*Pi) 590 | p.SmoothFilledArc(cx, cy, w, h, color, 0, stop) 591 | 592 | return 593 | } 594 | 595 | a := round(float64(w) / 2) 596 | b := round(float64(h) / 2) 597 | fcx := float64(cx) 598 | fcy := float64(cy) 599 | 600 | aaAngle := Atan((b * b) / (a * a) * Tan(0.25*Pi)) 601 | aaAngleX := a * Cos(aaAngle) 602 | aaAngleY := b * Sin(aaAngle) 603 | 604 | a -= 0.5 605 | b -= 0.5 606 | 607 | for i := float64(0); i < 4; i++ { 608 | if start < (i+1)*Pi/2 { 609 | if start > i*Pi/2 { 610 | if stop > (i+1)*Pi/2 { 611 | smootharcsegment(p, fcx, fcy, a, b, aaAngleX, aaAngleY, color, start, (i+1)*Pi/2, i) 612 | } else { 613 | smootharcsegment(p, fcx, fcy, a, b, aaAngleX, aaAngleY, color, start, stop, i) 614 | break 615 | } 616 | } else { 617 | if stop > (i+1)*Pi/2 { 618 | smootharcsegment(p, fcx, fcy, a, b, aaAngleX, aaAngleY, color, i*Pi/2, (i+1)*Pi/2, i) 619 | } else { 620 | smootharcsegment(p, fcx, fcy, a, b, aaAngleX, aaAngleY, color, i*Pi/2, stop, i) 621 | break 622 | } 623 | } 624 | } 625 | } 626 | } 627 | 628 | func (p *Image) SmoothFilledEllipse(cx, cy, w, h int, color Color) { 629 | p.SmoothFilledArc(cx, cy, w, h, color, 0, 2*Pi) 630 | } 631 | 632 | func searchfonts(dir string) (out []string) { 633 | files, e := ioutil.ReadDir(dir) 634 | if e == nil { 635 | for _, file := range files { 636 | if name := file.Name(); file.IsDir() { 637 | entry := filepath.Join(dir, name) 638 | out = append(out, searchfonts(entry)...) 639 | } else { 640 | if ext := filepath.Ext(name); ext != "" { 641 | ext := strings.ToLower(ext[1:]) 642 | whitelist := []string{"ttf", "otf", "cid", "cff", "pcf", "fnt", "bdr", "pfr", "pfa", "pfb", "afm"} 643 | 644 | for _, wext := range whitelist { 645 | if ext == wext { 646 | out = append(out, name) 647 | break 648 | } 649 | } 650 | } 651 | } 652 | } 653 | } 654 | 655 | return 656 | } 657 | 658 | func GetFonts() (list []string) { 659 | fontpath, pathseparator := "", "" 660 | 661 | switch runtime.GOOS { 662 | case "darwin": 663 | fontpath, pathseparator = "/usr/share/fonts/truetype:/System/Library/Fonts:/Library/Fonts", ":" 664 | 665 | case "windows": 666 | fontpath, pathseparator = `C:\WINDOWS\FONTS;C:\WINNT\FONTS`, ";" 667 | 668 | default: 669 | fontpath, pathseparator = "/usr/X11R6/lib/X11/fonts/TrueType:/usr/X11R6/lib/X11/fonts/truetype:"+ 670 | "/usr/X11R6/lib/X11/fonts/TTF:/usr/share/fonts/TrueType:/usr/share/fonts/truetype:"+ 671 | "/usr/openwin/lib/X11/fonts/TrueType:/usr/X11R6/lib/X11/fonts/Type1:/usr/lib/X11/fonts/Type1:"+ 672 | "/usr/openwin/lib/X11/fonts/Type1", ":" 673 | } 674 | 675 | if fontpath != "" { 676 | for _, dir := range strings.Split(fontpath, pathseparator) { 677 | list = append(list, searchfonts(dir)...) 678 | } 679 | } 680 | 681 | return 682 | } 683 | 684 | func (p *Image) filter(flt func(r, g, b, a, x, y int) (int, int, int, int)) { 685 | sx, sy := p.Sx(), p.Sy() 686 | for y := 0; y < sy; y++ { 687 | for x := 0; x < sx; x++ { 688 | rgba := p.ColorsForIndex(p.ColorAt(x, y)) 689 | r, g, b, a := flt(rgba["red"], rgba["green"], rgba["blue"], rgba["alpha"], x, y) 690 | 691 | newpxl := p.ColorAllocateAlpha(r, g, b, a) 692 | if newpxl == -1 { 693 | newpxl = p.ColorClosestAlpha(r, g, b, a) 694 | } 695 | 696 | p.SetPixel(x, y, newpxl) 697 | } 698 | } 699 | } 700 | 701 | func (p *Image) GrayScale() { 702 | p.filter(func(r, g, b, a, x, y int) (int, int, int, int) { 703 | c := (int)(.299*float64(r) + .587*float64(g) + .114*float64(b)) 704 | 705 | return c, c, c, a 706 | }) 707 | } 708 | 709 | func (p *Image) Negate() { 710 | p.filter(func(r, g, b, a, x, y int) (int, int, int, int) { 711 | r = 255 - r 712 | g = 255 - g 713 | b = 255 - b 714 | 715 | return r, g, b, a 716 | }) 717 | } 718 | 719 | func min(n1, n2 int) int { 720 | if n1 < n2 { 721 | return n1 722 | } 723 | 724 | return n2 725 | } 726 | 727 | func max(n1, n2 int) int { 728 | if n1 > n2 { 729 | return n1 730 | } 731 | 732 | return n2 733 | } 734 | 735 | func (p *Image) Brightness(brightness int) { 736 | if brightness == 0 { 737 | return 738 | } 739 | 740 | p.filter(func(r, g, b, a, x, y int) (int, int, int, int) { 741 | r = min(255, max(r+brightness, 0)) 742 | g = min(255, max(g+brightness, 0)) 743 | b = min(255, max(b+brightness, 0)) 744 | 745 | return r, g, b, a 746 | }) 747 | } 748 | 749 | func (p *Image) Contrast(contrast float64) { 750 | contrast = (100.0 - contrast) / 100.0 751 | contrast *= contrast 752 | 753 | corr := func(c int, contrast float64) int { 754 | f := float64(c) / 255.0 755 | f -= .5 756 | f *= contrast 757 | f += .5 758 | f *= 255.0 759 | 760 | return min(255, max(0, int(f))) 761 | } 762 | 763 | p.filter(func(r, g, b, a, x, y int) (int, int, int, int) { 764 | r = corr(r, contrast) 765 | g = corr(g, contrast) 766 | b = corr(b, contrast) 767 | 768 | return r, g, b, a 769 | }) 770 | } 771 | 772 | func (p *Image) Color(r, g, b, a int) { 773 | p.filter(func(ri, gi, bi, ai, x, y int) (int, int, int, int) { 774 | ri = max(0, min(255, r+ri)) 775 | gi = max(0, min(255, g+gi)) 776 | bi = max(0, min(255, b+bi)) 777 | ai = max(0, min(255, a+ai)) 778 | 779 | return ri, gi, bi, ai 780 | }) 781 | } 782 | 783 | func (p *Image) Convolution(filter [3][3]float32, filter_div, offset float32) { 784 | sx, sy := p.Sx(), p.Sy() 785 | srcback := CreateTrueColor(sx, sy) 786 | defer srcback.Destroy() 787 | 788 | srcback.SaveAlpha(true) 789 | newpxl := srcback.ColorAllocateAlpha(0, 0, 0, 127) 790 | srcback.Fill(0, 0, newpxl) 791 | 792 | p.Copy(srcback, 0, 0, 0, 0, sx, sy) 793 | 794 | var af func(p *Image, c int) int 795 | 796 | if p.TrueColor() { 797 | af = func(p *Image, c int) int { return (c & 0x7F000000) >> 24 } 798 | } else { 799 | af = func(p *Image, c int) int { return (int)((*p.img).alpha[c]) } 800 | } 801 | 802 | for y := 0; y < sy; y++ { 803 | for x := 0; x < sx; x++ { 804 | newr, newg, newb := float32(0), float32(0), float32(0) 805 | 806 | for j := 0; j < 3; j++ { 807 | yv := min(max(y-1+j, 0), sy-1) 808 | 809 | for i := 0; i < 3; i++ { 810 | pxl := srcback.ColorsForIndex(srcback.ColorAt(min(max(x-1+i, 0), sx-1), yv)) 811 | 812 | newr += float32(pxl["red"]) * filter[j][i] 813 | newg += float32(pxl["green"]) * filter[j][i] 814 | newb += float32(pxl["blue"]) * filter[j][i] 815 | } 816 | } 817 | 818 | newr = (newr / filter_div) + offset 819 | newg = (newg / filter_div) + offset 820 | newb = (newb / filter_div) + offset 821 | 822 | r := min(255, max(0, int(newr))) 823 | g := min(255, max(0, int(newg))) 824 | b := min(255, max(0, int(newb))) 825 | 826 | newa := af(srcback, int(srcback.ColorAt(x, y))) 827 | 828 | newpxl = p.ColorAllocateAlpha(r, g, b, newa) 829 | if newpxl == -1 { 830 | newpxl = p.ColorClosestAlpha(r, g, b, newa) 831 | } 832 | 833 | p.SetPixel(x, y, newpxl) 834 | } 835 | } 836 | } 837 | 838 | func (p *Image) GaussianBlur() { 839 | filter := [3][3]float32{{1.0, 2.0, 3.0}, {2.0, 4.0, 2.0}, {1.0, 2.0, 1.0}} 840 | p.Convolution(filter, 16, 0) 841 | } 842 | 843 | func (p *Image) EdgeDetectQuick() { 844 | filter := [3][3]float32{{-1.0, 0.0, -1.0}, {0.0, 4.0, 0.0}, {-1.0, 0.0, -1.0}} 845 | p.Convolution(filter, 1, 127) 846 | } 847 | 848 | func (p *Image) Emboss() { 849 | filter := [3][3]float32{{1.5, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, -1.5}} 850 | p.Convolution(filter, 1, 127) 851 | } 852 | 853 | func (p *Image) MeanRemoval() { 854 | filter := [3][3]float32{{-1, -1, -1}, {-1, 9, -1}, {-1, -1, -1}} 855 | p.Convolution(filter, 1, 0) 856 | } 857 | 858 | func (p *Image) Smooth(weight float32) { 859 | filter := [3][3]float32{{1, 1, 1}, {1, weight, 1}, {1, 1, 1}} 860 | p.Convolution(filter, weight+8, 0) 861 | } 862 | -------------------------------------------------------------------------------- /gd.go: -------------------------------------------------------------------------------- 1 | package gd 2 | 3 | // Evgeny Stepanischev. 2011. http://bolknote.ru/ imbolk@gmail.com 4 | 5 | /* 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #cgo LDFLAGS: -lgd -L/usr/local/lib/ 14 | #cgo CFLAGS: -I/usr/local/include/ 15 | 16 | // Avoid CGO bug https://github.com/golang/go/issues/19832 17 | #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" 18 | */ 19 | import "C" 20 | import . "unsafe" 21 | import "errors" 22 | 23 | type Image struct { 24 | img C.gdImagePtr 25 | getpixel func(p *Image, x, y int) Color 26 | } 27 | type Font struct{ fnt C.gdFontPtr } 28 | type Color int 29 | type Style int 30 | 31 | type Point struct { 32 | X, Y int 33 | } 34 | 35 | const ( 36 | ARCPIE Style = 0 37 | ARCCHORD Style = 1 << iota 38 | ARCNOFILL 39 | ARCEDGED 40 | ) 41 | 42 | const ( 43 | FONTTINY = iota 44 | FONTSMALL 45 | FONTMEDIUMBOLD 46 | FONTLARGE 47 | FONTGIANT 48 | ) 49 | 50 | func img(img C.gdImagePtr) *Image { 51 | if (int)((*img).trueColor) != 0 { 52 | return &Image{img: img, getpixel: gettruecolorpixel} 53 | } 54 | 55 | return &Image{img: img, getpixel: getpixel} 56 | } 57 | 58 | // http://php.net/manual/en/function.imagecreate.php 59 | func Create(sx, sy int) *Image { 60 | return img(C.gdImageCreate(C.int(sx), C.int(sy))) 61 | } 62 | 63 | // http://php.net/manual/en/function.imagecreatetruecolor.php 64 | func CreateTrueColor(sx, sy int) *Image { 65 | return img(C.gdImageCreateTrueColor(C.int(sx), C.int(sy))) 66 | } 67 | 68 | func CreateFromJpegPtr(imagebuffer []byte) *Image { 69 | return img(C.gdImageCreateFromJpegPtr(C.int(len(imagebuffer)), Pointer(&imagebuffer[0]))) 70 | } 71 | 72 | func CreateFromPngPtr(imagebuffer []byte) *Image { 73 | return img(C.gdImageCreateFromPngPtr(C.int(len(imagebuffer)), Pointer(&imagebuffer[0]))) 74 | } 75 | 76 | func CreateFromGifPtr(imagebuffer []byte) *Image { 77 | return img(C.gdImageCreateFromGifPtr(C.int(len(imagebuffer)), Pointer(&imagebuffer[0]))) 78 | } 79 | 80 | func CreateFromWebpPtr(imagebuffer []byte) *Image { 81 | return img(C.gdImageCreateFromWebpPtr(C.int(len(imagebuffer)), Pointer(&imagebuffer[0]))) 82 | } 83 | 84 | func ImageToJpegBuffer(p *Image, quality int) []byte { 85 | var imgSize int 86 | pimgSize := (*C.int)(Pointer(&imgSize)) 87 | 88 | buf := C.gdImageJpegPtr(p.img, pimgSize, C.int(quality)) 89 | defer C.gdFree(buf) 90 | 91 | return C.GoBytes(buf, *pimgSize) 92 | } 93 | 94 | func ImageToPngBuffer(p *Image) []byte { 95 | var imgSize int 96 | pimgSize := (*C.int)(Pointer(&imgSize)) 97 | 98 | buf := C.gdImagePngPtr(p.img, pimgSize) 99 | defer C.gdFree(buf) 100 | 101 | return C.GoBytes(buf, *pimgSize) 102 | } 103 | 104 | func ImageToGifBuffer(p *Image) []byte { 105 | var imgSize int 106 | pimgSize := (*C.int)(Pointer(&imgSize)) 107 | 108 | buf := C.gdImageGifPtr(p.img, pimgSize) 109 | defer C.gdFree(buf) 110 | 111 | return C.GoBytes(buf, *pimgSize) 112 | } 113 | 114 | func ImageToWebpBuffer(p *Image, quantization int) []byte { 115 | var imgSize int 116 | pimgSize := (*C.int)(Pointer(&imgSize)) 117 | 118 | buf := C.gdImageWebpPtrEx(p.img, pimgSize, C.int(quantization)) 119 | defer C.gdFree(buf) 120 | 121 | return C.GoBytes(buf, *pimgSize) 122 | } 123 | 124 | // http://php.net/manual/en/function.imagecreatefromjpeg.php 125 | func CreateFromJpeg(infile string) *Image { 126 | name, mode := C.CString(infile), C.CString("rb") 127 | 128 | defer C.free(Pointer(name)) 129 | defer C.free(Pointer(mode)) 130 | 131 | file := C.fopen(name, mode) 132 | 133 | if file != nil { 134 | defer C.fclose(file) 135 | 136 | return img(C.gdImageCreateFromJpeg(file)) 137 | } 138 | 139 | panic(errors.New("Error occurred while opening file.")) 140 | } 141 | 142 | // http://php.net/manual/en/function.imagecreatefromgif.php 143 | func CreateFromGif(infile string) *Image { 144 | name, mode := C.CString(infile), C.CString("rb") 145 | 146 | defer C.free(Pointer(name)) 147 | defer C.free(Pointer(mode)) 148 | 149 | file := C.fopen(name, mode) 150 | 151 | if file != nil { 152 | defer C.fclose(file) 153 | 154 | return img(C.gdImageCreateFromGif(file)) 155 | } 156 | 157 | panic(errors.New("Error occurred while opening file.")) 158 | } 159 | 160 | // http://php.net/manual/en/function.imagecreatefrompng.php 161 | func CreateFromPng(infile string) *Image { 162 | name, mode := C.CString(infile), C.CString("rb") 163 | 164 | defer C.free(Pointer(name)) 165 | defer C.free(Pointer(mode)) 166 | 167 | file := C.fopen(name, mode) 168 | 169 | if file != nil { 170 | defer C.fclose(file) 171 | 172 | return img(C.gdImageCreateFromPng(file)) 173 | } 174 | 175 | panic(errors.New("Error occurred while opening file.")) 176 | } 177 | 178 | // http://php.net/manual/en/function.imagecreatefromwbmp.php 179 | func CreateFromWbmp(infile string) *Image { 180 | name, mode := C.CString(infile), C.CString("rb") 181 | 182 | defer C.free(Pointer(name)) 183 | defer C.free(Pointer(mode)) 184 | 185 | file := C.fopen(name, mode) 186 | 187 | if file != nil { 188 | defer C.fclose(file) 189 | 190 | return img(C.gdImageCreateFromWBMP(file)) 191 | } 192 | 193 | panic(errors.New("Error occurred while opening file.")) 194 | } 195 | 196 | // http://php.net/manual/en/function.imagecreatefromwebp.php 197 | func CreateImageFromWebp(infile string) *Image { 198 | name, mode := C.CString(infile), C.CString("rb") 199 | 200 | defer C.free(Pointer(name)) 201 | defer C.free(Pointer(mode)) 202 | 203 | file := C.fopen(name, mode) 204 | 205 | if file != nil { 206 | defer C.fclose(file) 207 | 208 | return img(C.gdImageCreateFromWebp(file)) 209 | } 210 | 211 | panic(errors.New("Error occurred while opening file.")) 212 | } 213 | 214 | // http://php.net/manual/en/function.imagecreatefromxbm.php 215 | func CreateImageFromXbm(infile string) *Image { 216 | name, mode := C.CString(infile), C.CString("rb") 217 | 218 | defer C.free(Pointer(name)) 219 | defer C.free(Pointer(mode)) 220 | 221 | file := C.fopen(name, mode) 222 | 223 | if file != nil { 224 | defer C.fclose(file) 225 | 226 | return img(C.gdImageCreateFromXbm(file)) 227 | } 228 | 229 | panic(errors.New("Error occurred while opening file.")) 230 | } 231 | 232 | // http://php.net/manual/en/function.imagecreatefromxpm.php 233 | func CreateFromXpm(infile string) (im *Image) { 234 | defer func() { 235 | if e := recover(); e != nil { 236 | panic(errors.New("Error occurred while opening file.")) 237 | } 238 | }() 239 | 240 | name := C.CString(infile) 241 | defer C.free(Pointer(name)) 242 | 243 | return img(C.gdImageCreateFromXpm(name)) 244 | } 245 | 246 | // http://php.net/manual/en/function.imagedestroy.php 247 | func (p *Image) Destroy() { 248 | if p != nil && p.img != nil { 249 | C.gdImageDestroy(p.img) 250 | } 251 | } 252 | 253 | func (p *Image) SquareToCircle(radius int) *Image { 254 | return img(C.gdImageSquareToCircle(p.img, C.int(radius))) 255 | } 256 | 257 | // http://php.net/manual/en/function.imagejpeg.php 258 | func (p *Image) Jpeg(out string, quality int) { 259 | name, mode := C.CString(out), C.CString("wb") 260 | 261 | defer C.free(Pointer(name)) 262 | defer C.free(Pointer(mode)) 263 | 264 | file := C.fopen(name, mode) 265 | 266 | if file != nil { 267 | defer C.fclose(file) 268 | 269 | C.gdImageJpeg(p.img, file, C.int(quality)) 270 | } else { 271 | panic(errors.New("Error occurred while opening file for writing.")) 272 | } 273 | } 274 | 275 | // http://php.net/manual/en/function.imagepng.php 276 | func (p *Image) Png(out string) { 277 | name, mode := C.CString(out), C.CString("wb") 278 | 279 | defer C.free(Pointer(name)) 280 | defer C.free(Pointer(mode)) 281 | 282 | file := C.fopen(name, mode) 283 | 284 | if file != nil { 285 | defer C.fclose(file) 286 | 287 | C.gdImagePng(p.img, file) 288 | } else { 289 | panic(errors.New("Error occurred while opening file for writing.")) 290 | } 291 | } 292 | 293 | // http://php.net/manual/en/function.imagegif.php 294 | func (p *Image) Gif(out string) { 295 | name, mode := C.CString(out), C.CString("wb") 296 | 297 | defer C.free(Pointer(name)) 298 | defer C.free(Pointer(mode)) 299 | 300 | file := C.fopen(name, mode) 301 | 302 | if file != nil { 303 | defer C.fclose(file) 304 | 305 | C.gdImageGif(p.img, file) 306 | } else { 307 | panic(errors.New("Error occurred while opening file for writing.")) 308 | } 309 | } 310 | 311 | // http://php.net/manual/en/function.imagewebp.php 312 | func (p *Image) Webp(out string, quantization int) { 313 | name, mode := C.CString(out), C.CString("wb") 314 | 315 | defer C.free(Pointer(name)) 316 | defer C.free(Pointer(mode)) 317 | 318 | file := C.fopen(name, mode) 319 | 320 | if file != nil { 321 | defer C.fclose(file) 322 | 323 | C.gdImageWebpEx(p.img, file, C.int(quantization)) 324 | } else { 325 | panic(errors.New("Error occurred while opening file for writing.")) 326 | } 327 | } 328 | 329 | 330 | // http://php.net/manual/en/function.imagewbmp.php 331 | func (p *Image) Wbmp(out string, foreground Color) { 332 | name, mode := C.CString(out), C.CString("wb") 333 | 334 | defer C.free(Pointer(name)) 335 | defer C.free(Pointer(mode)) 336 | 337 | file := C.fopen(name, mode) 338 | 339 | if file != nil { 340 | defer C.fclose(file) 341 | 342 | C.gdImageWBMP(p.img, C.int(foreground), file) 343 | } else { 344 | panic(errors.New("Error occurred while opening file for writing.")) 345 | } 346 | } 347 | 348 | // http://php.net/manual/en/function.imagecolortransparent.php 349 | func (p *Image) ColorTransparent(color Color) { 350 | C.gdImageColorTransparent(p.img, C.int(color)) 351 | } 352 | 353 | // http://php.net/manual/en/function.imagepalettecopy.php 354 | func (p *Image) PaletteCopy(dst *Image) { 355 | C.gdImagePaletteCopy(dst.img, p.img) 356 | } 357 | 358 | // http://php.net/manual/en/function.imagecopyresampled.php 359 | func (p *Image) CopyResampled(dst *Image, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH int) { 360 | C.gdImageCopyResampled(dst.img, p.img, C.int(dstX), C.int(dstY), C.int(srcX), C.int(srcY), 361 | C.int(dstW), C.int(dstH), C.int(srcW), C.int(srcH)) 362 | } 363 | 364 | // http://php.net/manual/en/function.imagecopyresized.php 365 | func (p *Image) CopyResized(dst *Image, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH int) { 366 | C.gdImageCopyResized(dst.img, p.img, C.int(dstX), C.int(dstY), C.int(srcX), C.int(srcY), 367 | C.int(dstW), C.int(dstH), C.int(srcW), C.int(srcH)) 368 | } 369 | 370 | // http://php.net/manual/en/function.imagecopymerge.php 371 | func (p *Image) CopyMerge(dst *Image, dstX, dstY, srcX, srcY, w, h, pct int) { 372 | C.gdImageCopyMerge(dst.img, p.img, C.int(dstX), C.int(dstY), C.int(srcX), C.int(srcY), 373 | C.int(w), C.int(h), C.int(pct)) 374 | } 375 | 376 | // http://php.net/manual/en/function.imagecopymergegray.php 377 | func (p *Image) CopyMergeGray(dst *Image, dstX, dstY, srcX, srcY, w, h, pct int) { 378 | C.gdImageCopyMergeGray(dst.img, p.img, C.int(dstX), C.int(dstY), C.int(srcX), C.int(srcY), 379 | C.int(w), C.int(h), C.int(pct)) 380 | } 381 | 382 | // http://php.net/manual/en/function.imagecopy.php 383 | func (p *Image) Copy(dst *Image, dstX, dstY, srcX, srcY, w, h int) { 384 | C.gdImageCopy(dst.img, p.img, C.int(dstX), C.int(dstY), C.int(srcX), C.int(srcY), C.int(w), C.int(h)) 385 | } 386 | 387 | func (p *Image) CopyRotated(dst *Image, dstX, dstY, srcX, srcY, srcWidth, srcHeight, angle int) { 388 | C.gdImageCopyRotated(dst.img, p.img, C.double(dstX), C.double(dstY), C.int(srcX), C.int(srcY), 389 | C.int(srcWidth), C.int(srcHeight), C.int(angle)) 390 | } 391 | 392 | // http://php.net/manual/en/function.imagecolorallocate.php 393 | func (p *Image) ColorAllocate(r, g, b int) Color { 394 | return (Color)(C.gdImageColorAllocate(p.img, C.int(r), C.int(g), C.int(b))) 395 | } 396 | 397 | // http://php.net/manual/en/function.imagecolorallocatealpha.php 398 | func (p *Image) ColorAllocateAlpha(r, g, b, a int) Color { 399 | return (Color)(C.gdImageColorAllocateAlpha(p.img, C.int(r), C.int(g), C.int(b), C.int(a))) 400 | } 401 | 402 | // http://php.net/manual/en/function.imagecolorclosest.php 403 | func (p *Image) ColorClosest(r, g, b int) Color { 404 | return (Color)(C.gdImageColorClosest(p.img, C.int(r), C.int(g), C.int(b))) 405 | } 406 | 407 | // http://php.net/manual/en/function.imagecolorclosestalpha.php 408 | func (p *Image) ColorClosestAlpha(r, g, b, a int) Color { 409 | return (Color)(C.gdImageColorClosestAlpha(p.img, C.int(r), C.int(g), C.int(b), C.int(a))) 410 | } 411 | 412 | // http://php.net/manual/en/function.imagecolorclosesthwb.php 413 | func (p *Image) ColorClosestHWB(r, g, b int) Color { 414 | return (Color)(C.gdImageColorClosestHWB(p.img, C.int(r), C.int(g), C.int(b))) 415 | } 416 | 417 | // http://php.net/manual/en/function.imagecolorexact.php 418 | func (p *Image) ColorExact(r, g, b int) Color { 419 | return (Color)(C.gdImageColorExact(p.img, C.int(r), C.int(g), C.int(b))) 420 | } 421 | 422 | // http://php.net/manual/en/function.imagecolorexactalpha.php 423 | func (p *Image) ColorExactAlpha(r, g, b, a int) Color { 424 | return (Color)(C.gdImageColorExactAlpha(p.img, C.int(r), C.int(g), C.int(b), C.int(a))) 425 | } 426 | 427 | // http://php.net/manual/en/function.imagecolorresolve.php 428 | func (p *Image) ColorResolve(r, g, b int) Color { 429 | return (Color)(C.gdImageColorResolve(p.img, C.int(r), C.int(g), C.int(b))) 430 | } 431 | 432 | // http://php.net/manual/en/function.imagecolorresolvealpha.php 433 | func (p *Image) ColorResolveAlpha(r, g, b, a int) Color { 434 | return (Color)(C.gdImageColorResolveAlpha(p.img, C.int(r), C.int(g), C.int(b), C.int(a))) 435 | } 436 | 437 | // http://php.net/manual/en/function.imagecolordeallocate.php 438 | func (p *Image) ColorDeallocate(color Color) { 439 | C.gdImageColorDeallocate(p.img, C.int(color)) 440 | } 441 | 442 | // http://php.net/manual/en/function.imagefill.php 443 | func (p *Image) Fill(x, y int, c Color) { 444 | C.gdImageFill(p.img, C.int(x), C.int(y), C.int(c)) 445 | } 446 | 447 | // http://php.net/manual/en/function.imagefilledarc.php 448 | func (p *Image) FilledArc(cx, cy, w, h, s, e, color Color, style Style) { 449 | C.gdImageFilledArc(p.img, C.int(cx), C.int(cy), C.int(w), C.int(h), C.int(s), 450 | C.int(e), C.int(color), C.int(style)) 451 | } 452 | 453 | // http://php.net/manual/en/function.imagearc.php 454 | func (p *Image) Arc(cx, cy, w, h, s, e int, color Color) { 455 | C.gdImageArc(p.img, C.int(cx), C.int(cy), C.int(w), C.int(h), 456 | C.int(s), C.int(e), C.int(color)) 457 | } 458 | 459 | // http://php.net/manual/en/function.imagefilledellipse.php 460 | func (p *Image) FilledEllipse(cx, cy, w, h int, color Color) { 461 | C.gdImageFilledEllipse(p.img, C.int(cx), C.int(cy), C.int(w), C.int(h), C.int(color)) 462 | } 463 | 464 | // http://php.net/manual/en/function.imageellipse.php 465 | func (p *Image) Ellipse(cx, cy, w, h int, color Color) { 466 | C.gdImageEllipse(p.img, C.int(cx), C.int(cy), C.int(w), C.int(h), C.int(color)) 467 | } 468 | 469 | // http://php.net/manual/en/function.imagefilltoborder.php 470 | func (p *Image) FillToBorder(x, y, border, color Color) { 471 | C.gdImageFillToBorder(p.img, C.int(x), C.int(y), C.int(border), C.int(color)) 472 | } 473 | 474 | func (p *Image) Sharpen(pct int) { 475 | C.gdImageSharpen(p.img, C.int(pct)) 476 | } 477 | 478 | // http://php.net/manual/en/function.imagesx.php 479 | func (p *Image) Sx() int { 480 | return (int)((*p.img).sx) 481 | } 482 | 483 | // http://php.net/manual/en/function.imagesy.php 484 | func (p *Image) Sy() int { 485 | return (int)((*p.img).sy) 486 | } 487 | 488 | func (p *Image) GetInterlaced() bool { 489 | return (int)((*p.img).interlace) != 0 490 | } 491 | 492 | // http://php.net/manual/en/function.imagecolorstotal.php 493 | func (p *Image) ColorsTotal() int { 494 | return (int)((*p.img).colorsTotal) 495 | } 496 | 497 | func (p *Image) TrueColor() bool { 498 | return (int)((*p.img).trueColor) != 0 499 | } 500 | 501 | // http://php.net/manual/en/function.imagesetpixel.php 502 | func (p *Image) SetPixel(x, y int, color Color) { 503 | C.gdImageSetPixel(p.img, C.int(x), C.int(y), C.int(color)) 504 | } 505 | 506 | func getpixel(p *Image, x, y int) Color { 507 | return (Color)(C.gdImageGetPixel(p.img, C.int(x), C.int(y))) 508 | } 509 | 510 | func gettruecolorpixel(p *Image, x, y int) Color { 511 | return (Color)(C.gdImageGetPixel(p.img, C.int(x), C.int(y))) 512 | } 513 | 514 | // http://php.net/manual/en/function.imagecolorat.php 515 | func (p *Image) ColorAt(x, y int) Color { 516 | return (*p).getpixel(p, x, y) 517 | } 518 | 519 | func (p *Image) AABlend() { 520 | C.gdImageAABlend(p.img) 521 | } 522 | 523 | // http://php.net/manual/en/function.imageline.php 524 | func (p *Image) Line(x1, y1, x2, y2 int, color Color) { 525 | C.gdImageLine(p.img, C.int(x1), C.int(y1), C.int(x2), C.int(y2), C.int(color)) 526 | } 527 | 528 | // http://php.net/manual/en/function.imagedashedline.php 529 | func (p *Image) DashedLine(x1, y1, x2, y2 int, color Color) { 530 | C.gdImageDashedLine(p.img, C.int(x1), C.int(y1), C.int(x2), C.int(y2), C.int(color)) 531 | } 532 | 533 | func (p *Image) Rectangle(x1, y1, x2, y2 int, color Color) { 534 | C.gdImageRectangle(p.img, C.int(x1), C.int(y1), C.int(x2), C.int(y2), C.int(color)) 535 | } 536 | 537 | // http://php.net/manual/en/function.imagefilledrectangle.php 538 | func (p *Image) FilledRectangle(x1, y1, x2, y2 int, color Color) { 539 | C.gdImageFilledRectangle(p.img, C.int(x1), C.int(y1), C.int(x2), C.int(y2), C.int(color)) 540 | } 541 | 542 | // http://php.net/manual/en/function.imagesavealpha.php 543 | func (p *Image) SaveAlpha(saveflag bool) { 544 | C.gdImageSaveAlpha(p.img, map[bool]C.int{true: 1, false: 0}[saveflag]) 545 | } 546 | 547 | // http://php.net/manual/en/function.imagealphablending.php 548 | func (p *Image) AlphaBlending(blendmode bool) { 549 | C.gdImageAlphaBlending(p.img, map[bool]C.int{true: 1, false: 0}[blendmode]) 550 | } 551 | 552 | // http://php.net/manual/en/function.imageinterlace.php 553 | func (p *Image) Interlace(interlacemode bool) { 554 | C.gdImageInterlace(p.img, map[bool]C.int{true: 1, false: 0}[interlacemode]) 555 | } 556 | 557 | // http://php.net/manual/en/function.imagesetthickness.php 558 | func (p *Image) SetThickness(thickness int) { 559 | C.gdImageSetThickness(p.img, C.int(thickness)) 560 | } 561 | 562 | // http://php.net/manual/en/function.imagetruecolortopalette.php 563 | func (p *Image) TrueColorToPalette(ditherFlag bool, colorsWanted int) { 564 | C.gdImageTrueColorToPalette(p.img, map[bool]C.int{true: 1, false: 0}[ditherFlag], C.int(colorsWanted)) 565 | } 566 | 567 | // http://php.net/manual/en/function.imagesetstyle.php 568 | func (p *Image) SetStyle(style ...Color) { 569 | C.gdImageSetStyle(p.img, (*C.int)(Pointer(&style)), C.int(len(style))) 570 | } 571 | 572 | func (p *Image) SetAntiAliased(c Color) { 573 | C.gdImageSetAntiAliased(p.img, C.int(c)) 574 | } 575 | 576 | func (p *Image) SetAntiAliasedDontBlend(c Color, dont_blend bool) { 577 | C.gdImageSetAntiAliasedDontBlend(p.img, C.int(c), map[bool]C.int{true: 1, false: 0}[dont_blend]) 578 | } 579 | 580 | // http://php.net/manual/en/function.imagesettile.php 581 | func (p *Image) SetTile(tile Image) { 582 | C.gdImageSetTile(p.img, tile.img) 583 | } 584 | 585 | // http://php.net/manual/en/function.imagesetbrush.php 586 | func (p *Image) SetBrush(brush Image) { 587 | C.gdImageSetBrush(p.img, brush.img) 588 | } 589 | 590 | func GetFont(size byte) *Font { 591 | switch size { 592 | case FONTTINY: 593 | return &Font{fnt: C.gdFontGetTiny()} 594 | case FONTSMALL: 595 | return &Font{fnt: C.gdFontGetSmall()} 596 | case FONTMEDIUMBOLD: 597 | return &Font{fnt: C.gdFontGetMediumBold()} 598 | case FONTLARGE: 599 | return &Font{fnt: C.gdFontGetLarge()} 600 | case FONTGIANT: 601 | return &Font{fnt: C.gdFontGetGiant()} 602 | } 603 | 604 | panic(errors.New("Invalid font size")) 605 | } 606 | 607 | // http://php.net/manual/en/function.imagechar.php 608 | func (p *Image) Char(font *Font, x, y int, c string, color Color) { 609 | C.gdImageChar(p.img, (*font).fnt, C.int(x), C.int(y), C.int(([]byte(c))[0]), C.int(color)) 610 | } 611 | 612 | // http://php.net/manual/en/function.imagecharup.php 613 | func (p *Image) CharUp(font *Font, x, y int, c string, color Color) { 614 | C.gdImageCharUp(p.img, (*font).fnt, C.int(x), C.int(y), C.int(([]byte(c))[0]), C.int(color)) 615 | } 616 | 617 | // http://php.net/manual/en/function.imagestring.php 618 | func (p *Image) String(font *Font, x, y int, s string, color Color) { 619 | str := Pointer(C.CString(s)) 620 | C.gdImageString(p.img, (*font).fnt, C.int(x), C.int(y), (*C.uchar)(str), C.int(color)) 621 | C.free(str) 622 | } 623 | 624 | // http://php.net/manual/en/function.imagestringup.php 625 | func (p *Image) StringUp(font *Font, x, y int, s string, color Color) { 626 | str := Pointer(C.CString(s)) 627 | C.gdImageStringUp(p.img, (*font).fnt, C.int(x), C.int(y), (*C.uchar)(str), C.int(color)) 628 | C.free(str) 629 | } 630 | 631 | func (p *Image) StringFT(fg Color, fontname string, ptsize, angle float64, x, y int, str string) (brect [8]int32) { 632 | C.gdFontCacheSetup() 633 | defer C.gdFontCacheShutdown() 634 | 635 | cfontname, cstr := C.CString(fontname), C.CString(str) 636 | 637 | C.gdImageStringFT(p.img, (*C.int)(Pointer(&brect)), C.int(fg), cfontname, C.double(ptsize), 638 | C.double(angle), C.int(x), C.int(y), cstr) 639 | 640 | C.free(Pointer(cfontname)) 641 | C.free(Pointer(cstr)) 642 | 643 | return 644 | } 645 | 646 | func pointsTogdPoints(points []Point) C.gdPointPtr { 647 | const maxlen = 1 << 30 // ought to be enough for anybody 648 | alen := len(points) 649 | 650 | if alen > maxlen { 651 | panic("Too many arguments") 652 | } 653 | 654 | mem := C.calloc(C.size_t(alen), C.sizeof_gdPoint) 655 | gdpoints := (*[maxlen]C.gdPoint)(mem) 656 | 657 | for i, v := range points { 658 | gdpoints[i] = C.gdPoint{C.int(v.X), C.int(v.Y)} 659 | } 660 | 661 | return C.gdPointPtr(mem) 662 | } 663 | 664 | // http://php.net/manual/en/function.imagepolygon.php 665 | func (p *Image) Polygon(points []Point, c Color) { 666 | gdpoints := pointsTogdPoints(points) 667 | C.gdImagePolygon(p.img, gdpoints, C.int(len(points)), C.int(c)) 668 | C.free(Pointer(gdpoints)) 669 | } 670 | 671 | // http://php.net/manual/en/function.imageopenpolygon.php 672 | func (p *Image) OpenPolygon(points []Point, c Color) { 673 | gdpoints := pointsTogdPoints(points) 674 | C.gdImageOpenPolygon(p.img, gdpoints, C.int(len(points)), C.int(c)) 675 | C.free(Pointer(gdpoints)) 676 | } 677 | 678 | // http://php.net/manual/en/function.imagefilledpolygon.php 679 | func (p *Image) FilledPolygon(points []Point, c Color) { 680 | gdpoints := pointsTogdPoints(points) 681 | C.gdImageFilledPolygon(p.img, gdpoints, C.int(len(points)), C.int(c)) 682 | C.free(Pointer(gdpoints)) 683 | } 684 | 685 | // http://php.net/manual/en/function.imagecolorsforindex.php 686 | func (p *Image) ColorsForIndex(index Color) map[string]int { 687 | if p.TrueColor() { 688 | return map[string]int{ 689 | "alpha": (int(index) & 0x7F000000) >> 24, 690 | "red": (int(index) & 0xFF0000) >> 16, 691 | "green": (int(index) & 0x00FF00) >> 8, 692 | "blue": (int(index) & 0x0000FF), 693 | } 694 | } 695 | 696 | return map[string]int{ 697 | "red": (int)((*p.img).red[index]), 698 | "green": (int)((*p.img).green[index]), 699 | "blue": (int)((*p.img).blue[index]), 700 | "alpha": (int)((*p.img).alpha[index]), 701 | } 702 | } 703 | 704 | --------------------------------------------------------------------------------