| // Copyright (C) 2015 The Android Open Source Project |
| // |
| // 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 client |
| |
| import ( |
| "fmt" |
| "sort" |
| |
| "android.googlesource.com/platform/tools/gpu/service" |
| "android.googlesource.com/platform/tools/gpu/service/path" |
| "android.googlesource.com/platform/tools/gpu/task" |
| "github.com/google/gxui" |
| "github.com/google/gxui/math" |
| ) |
| |
| const kFilmStripAdapterItemWidth = 200 |
| const kFilmStripAdapterItemHeight = 150 |
| |
| type FilmStripAdapter struct { |
| gxui.AdapterBase |
| appCtx *ApplicationContext |
| device *path.Device |
| capture *path.Capture |
| frames []uint64 |
| } |
| |
| func CreateFilmStripAdapter(appCtx *ApplicationContext) *FilmStripAdapter { |
| return &FilmStripAdapter{appCtx: appCtx} |
| } |
| |
| func (a *FilmStripAdapter) UpdateFrames(capture *path.Capture, frames []uint64) { |
| a.capture = capture |
| a.frames = frames |
| a.DataReplaced() |
| } |
| |
| func (a *FilmStripAdapter) UpdateDevice(device *path.Device) { |
| a.device = device |
| a.DataReplaced() |
| } |
| |
| func (a *FilmStripAdapter) Size(theme gxui.Theme) math.Size { |
| return math.Size{W: kFilmStripAdapterItemWidth, H: kFilmStripAdapterItemHeight} |
| } |
| |
| func (a *FilmStripAdapter) ItemAt(index int) gxui.AdapterItem { |
| return a.frames[index] |
| } |
| |
| func (a *FilmStripAdapter) ItemIndex(item gxui.AdapterItem) int { |
| index := item.(uint64) |
| return sort.Search(len(a.frames), func(i int) bool { |
| return a.frames[i] >= index |
| }) |
| } |
| |
| func (a *FilmStripAdapter) Count() int { |
| return len(a.frames) |
| } |
| |
| func (a *FilmStripAdapter) Create(theme gxui.Theme, index int) gxui.Control { |
| w, h := kFilmStripAdapterItemWidth, kFilmStripAdapterItemHeight |
| atomIndex := a.frames[index] |
| p := a.capture.Atoms().Index(atomIndex) |
| |
| i := theme.CreateImage() |
| i.SetAspectMode(gxui.AspectCorrectCrop) |
| i.SetExplicitSize(math.Size{W: w, H: h}) |
| |
| b := theme.CreateButton() |
| b.SetDirection(gxui.TopToBottom) |
| b.SetText(fmt.Sprintf("%.6d - Frame %d", atomIndex, index)) |
| b.SetPadding(math.Spacing{L: 3, T: 3, R: 3, B: 3}) |
| b.SetMargin(math.Spacing{L: 3, T: 3, R: 3, B: 3}) |
| b.OnClick(func(gxui.MouseEvent) { a.appCtx.events.Select(p) }) |
| b.AddChild(i) |
| |
| t := task.New() |
| b.OnAttach(func() { t.Run(updateThumbnail{a.appCtx, a.device, p, i}) }) |
| b.OnDetach(t.Cancel) |
| |
| return b |
| } |
| |
| type updateThumbnail struct { |
| context *ApplicationContext |
| device *path.Device |
| after *path.Atom |
| image gxui.Image |
| } |
| |
| func (t updateThumbnail) Run(c task.CancelSignal) { |
| settings := service.RenderSettings{ |
| MaxWidth: kFilmStripAdapterItemWidth, |
| MaxHeight: kFilmStripAdapterItemHeight, |
| } |
| if w, h, d, err := t.context.rpc.RequestColorBuffer(t.device, t.after, settings); err == nil { |
| c.Check() |
| t.context.Run(func() { |
| tex := NewColorTexture(t.context.theme.Driver(), w, h, d) |
| t.image.SetTexture(tex) |
| t.context.toolTipController.AddToolTip(t.image, 0.7, func(math.Point) gxui.Control { |
| large := t.context.theme.CreateImage() |
| large.SetTexture(tex) |
| large.SetAspectMode(gxui.AspectCorrectLetterbox) |
| return large |
| }) |
| }) |
| } |
| } |