mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
colorm: Add ColorM.ChangeHSV and an example
This commit is contained in:
parent
05f52b5e57
commit
723876feba
71
colorm.go
71
colorm.go
@ -104,19 +104,57 @@ func (c *ColorM) Translate(r, g, b, a float64) {
|
|||||||
|
|
||||||
// RotateHue rotates the hue.
|
// RotateHue rotates the hue.
|
||||||
func (c *ColorM) RotateHue(theta float64) {
|
func (c *ColorM) RotateHue(theta float64) {
|
||||||
sin, cos := math.Sincos(theta)
|
c.ChangeHSV(theta, 1, 1)
|
||||||
v1 := cos + (1.0-cos)/3.0
|
}
|
||||||
v2 := (1.0/3.0)*(1.0-cos) - math.Sqrt(1.0/3.0)*sin
|
|
||||||
v3 := (1.0/3.0)*(1.0-cos) + math.Sqrt(1.0/3.0)*sin
|
var (
|
||||||
|
// The YCbCr value ranges are:
|
||||||
|
// Y: [ 0 - 1 ]
|
||||||
|
// Cb: [-0.5 - 0.5]
|
||||||
|
// Cr: [-0.5 - 0.5]
|
||||||
|
|
||||||
|
rgbToYCbCr = ColorM{
|
||||||
|
initialized: true,
|
||||||
|
es: [ColorMDim - 1][ColorMDim]float64{
|
||||||
|
{0.2990, 0.5870, 0.1140, 0, 0},
|
||||||
|
{-0.1687, -0.3313, 0.5000, 0, 0},
|
||||||
|
{0.5000, -0.4187, -0.0813, 0, 0},
|
||||||
|
{0, 0, 0, 1, 0},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
yCbCrToRgb = ColorM{
|
||||||
|
initialized: true,
|
||||||
|
es: [ColorMDim - 1][ColorMDim]float64{
|
||||||
|
{1, 0, 1.40200, 0, 0},
|
||||||
|
{1, -0.34414, -0.71414, 0, 0},
|
||||||
|
{1, 1.77200, 0, 0, 0},
|
||||||
|
{0, 0, 0, 1, 0},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChangeHSV changes HSV (Hue-Saturation-Value) values.
|
||||||
|
// hueTheta is a radian value to ratate hue.
|
||||||
|
// saturationScale is a value to scale saturation.
|
||||||
|
// valueScale is a value to scale value (a.k.a. brightness).
|
||||||
|
//
|
||||||
|
// This conversion uses RGB to/from YCrCb conversion.
|
||||||
|
func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float64, valueScale float64) {
|
||||||
|
sin, cos := math.Sincos(hueTheta)
|
||||||
|
c.Concat(rgbToYCbCr)
|
||||||
c.Concat(ColorM{
|
c.Concat(ColorM{
|
||||||
initialized: true,
|
initialized: true,
|
||||||
es: [ColorMDim - 1][ColorMDim]float64{
|
es: [ColorMDim - 1][ColorMDim]float64{
|
||||||
{v1, v2, v3, 0, 0},
|
{1, 0, 0, 0, 0},
|
||||||
{v3, v1, v2, 0, 0},
|
{0, cos, -sin, 0, 0},
|
||||||
{v2, v3, v1, 0, 0},
|
{0, sin, cos, 0, 0},
|
||||||
{0, 0, 0, 1, 0},
|
{0, 0, 0, 1, 0},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
s := saturationScale
|
||||||
|
v := valueScale
|
||||||
|
c.Scale(v, s*v, s*v, 1)
|
||||||
|
c.Concat(yCbCrToRgb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetElement sets an element at (i, j).
|
// SetElement sets an element at (i, j).
|
||||||
@ -127,20 +165,15 @@ func (c *ColorM) SetElement(i, j int, element float64) {
|
|||||||
c.es[i][j] = element
|
c.es[i][j] = element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var monochrome ColorM
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
monochrome.ChangeHSV(0, 0, 1)
|
||||||
|
}
|
||||||
|
|
||||||
// Monochrome returns a color matrix to make an image monochrome.
|
// Monochrome returns a color matrix to make an image monochrome.
|
||||||
func Monochrome() ColorM {
|
func Monochrome() ColorM {
|
||||||
const r = 6968.0 / 32768.0
|
return monochrome
|
||||||
const g = 23434.0 / 32768.0
|
|
||||||
const b = 2366.0 / 32768.0
|
|
||||||
return ColorM{
|
|
||||||
initialized: true,
|
|
||||||
es: [ColorMDim - 1][ColorMDim]float64{
|
|
||||||
{r, g, b, 0, 0},
|
|
||||||
{r, g, b, 0, 0},
|
|
||||||
{r, g, b, 0, 0},
|
|
||||||
{0, 0, 0, 1, 0},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated as of 1.2.0-alpha. Use Scale instead.
|
// Deprecated as of 1.2.0-alpha. Use Scale instead.
|
||||||
|
104
examples/hsv/main.go
Normal file
104
examples/hsv/main.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// Copyright 2016 Hajime Hoshi
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
_ "image/jpeg"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten"
|
||||||
|
"github.com/hajimehoshi/ebiten/ebitenutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
screenWidth = 320
|
||||||
|
screenHeight = 240
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hueInt = 0
|
||||||
|
saturationInt = 128
|
||||||
|
valueInt = 128
|
||||||
|
gophersImage *ebiten.Image
|
||||||
|
)
|
||||||
|
|
||||||
|
func clamp(v, min, max int) int {
|
||||||
|
if min > max {
|
||||||
|
panic("min must <= max")
|
||||||
|
}
|
||||||
|
if v < min {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
if max < v {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(screen *ebiten.Image) error {
|
||||||
|
if ebiten.IsKeyPressed(ebiten.KeyQ) {
|
||||||
|
hueInt--
|
||||||
|
}
|
||||||
|
if ebiten.IsKeyPressed(ebiten.KeyW) {
|
||||||
|
hueInt++
|
||||||
|
}
|
||||||
|
if ebiten.IsKeyPressed(ebiten.KeyA) {
|
||||||
|
saturationInt--
|
||||||
|
}
|
||||||
|
if ebiten.IsKeyPressed(ebiten.KeyS) {
|
||||||
|
saturationInt++
|
||||||
|
}
|
||||||
|
if ebiten.IsKeyPressed(ebiten.KeyZ) {
|
||||||
|
valueInt--
|
||||||
|
}
|
||||||
|
if ebiten.IsKeyPressed(ebiten.KeyX) {
|
||||||
|
valueInt++
|
||||||
|
}
|
||||||
|
hueInt = clamp(hueInt, -256, 256)
|
||||||
|
saturationInt = clamp(saturationInt, 0, 256)
|
||||||
|
valueInt = clamp(valueInt, 0, 256)
|
||||||
|
|
||||||
|
w, h := gophersImage.Size()
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
op.GeoM.Translate(float64(screenWidth-w)/2, float64(screenHeight-h)/2)
|
||||||
|
hue := float64(hueInt) * 2 * math.Pi / 128
|
||||||
|
saturation := float64(saturationInt) / 128
|
||||||
|
value := float64(valueInt) / 128
|
||||||
|
op.ColorM.ChangeHSV(hue, saturation, value)
|
||||||
|
if err := screen.DrawImage(gophersImage, op); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := fmt.Sprintf(`Hue: %0.2f [Q][W]
|
||||||
|
Saturation: %0.2f [A][S]
|
||||||
|
Value: %0.2f [Z][X]`, hue, saturation, value)
|
||||||
|
if err := ebitenutil.DebugPrint(screen, msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
gophersImage, _, err = ebitenutil.NewImageFromFile("_resources/images/gophers.jpg", ebiten.FilterNearest)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "HSV (Ebiten Demo)"); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user