Canvas.cxx 19.7 KB
Newer Older
1
2
3
4
//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
5
//
6
7
8
9
10
11
12
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//============================================================================

#include <vtkm/rendering/Canvas.h>

13
#include <vtkm/cont/ArrayHandleCounting.h>
14
#include <vtkm/cont/TryExecute.h>
15
16
#include <vtkm/rendering/BitmapFontFactory.h>
#include <vtkm/rendering/DecodePNG.h>
17
#include <vtkm/rendering/EncodePNG.h>
18
#include <vtkm/rendering/LineRenderer.h>
19
#include <vtkm/rendering/TextRenderer.h>
20
#include <vtkm/rendering/WorldAnnotator.h>
21
22
23
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/WorkletMapField.h>

24
25
#include <vtkm/cont/ColorTable.hxx>

26
#include <fstream>
27
#include <iostream>
28

29
30
31
32
namespace vtkm
{
namespace rendering
{
33
34
35
36
37
namespace internal
{

struct ClearBuffers : public vtkm::worklet::WorkletMapField
{
38
  using ControlSignature = void(FieldOut, FieldOut);
39
  using ExecutionSignature = void(_1, _2);
40
41
42
43
44

  VTKM_CONT
  ClearBuffers() {}

  VTKM_EXEC
45
  void operator()(vtkm::Vec4f_32& color, vtkm::Float32& depth) const
46
47
48
49
50
51
52
  {
    color[0] = 0.f;
    color[1] = 0.f;
    color[2] = 0.f;
    color[3] = 0.f;
    // The depth is set to slightly larger than 1.0f, ensuring this color value always fails a
    // depth check
53
    depth = VTKM_DEFAULT_CANVAS_DEPTH;
54
55
56
57
58
  }
}; // struct ClearBuffers

struct BlendBackground : public vtkm::worklet::WorkletMapField
{
59
  vtkm::Vec4f_32 BackgroundColor;
60
61

  VTKM_CONT
62
  BlendBackground(const vtkm::Vec4f_32& backgroundColor)
63
64
65
66
    : BackgroundColor(backgroundColor)
  {
  }

67
  using ControlSignature = void(FieldInOut);
68
  using ExecutionSignature = void(_1);
69

70
  VTKM_EXEC void operator()(vtkm::Vec4f_32& color) const
71
72
73
74
75
76
77
78
79
80
81
82
  {
    if (color[3] >= 1.f)
      return;

    vtkm::Float32 alpha = BackgroundColor[3] * (1.f - color[3]);
    color[0] = color[0] + BackgroundColor[0] * alpha;
    color[1] = color[1] + BackgroundColor[1] * alpha;
    color[2] = color[2] + BackgroundColor[2] * alpha;
    color[3] = alpha + color[3];
  }
}; // struct BlendBackground

83
84
struct DrawColorSwatch : public vtkm::worklet::WorkletMapField
{
85
  using ControlSignature = void(FieldIn, WholeArrayInOut);
86
  using ExecutionSignature = void(_1, _2);
87
88

  VTKM_CONT
89
  DrawColorSwatch(vtkm::Id2 dims, vtkm::Id2 xBounds, vtkm::Id2 yBounds, const vtkm::Vec4f_32 color)
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    : Color(color)
  {
    ImageWidth = dims[0];
    ImageHeight = dims[1];
    SwatchBottomLeft[0] = xBounds[0];
    SwatchBottomLeft[1] = yBounds[0];
    SwatchWidth = xBounds[1] - xBounds[0];
    SwatchHeight = yBounds[1] - yBounds[0];
  }

  template <typename FrameBuffer>
  VTKM_EXEC void operator()(const vtkm::Id& index, FrameBuffer& frameBuffer) const
  {
    // local bar coord
    vtkm::Id x = index % SwatchWidth;
Matt Larsen's avatar
Matt Larsen committed
105
    vtkm::Id y = index / SwatchWidth;
106
107
108
109
110
111
112
113
114
115
116
117
118
119

    // offset to global image coord
    x += SwatchBottomLeft[0];
    y += SwatchBottomLeft[1];

    vtkm::Id offset = y * ImageWidth + x;
    frameBuffer.Set(offset, Color);
  }

  vtkm::Id ImageWidth;
  vtkm::Id ImageHeight;
  vtkm::Id2 SwatchBottomLeft;
  vtkm::Id SwatchWidth;
  vtkm::Id SwatchHeight;
120
  const vtkm::Vec4f_32 Color;
121
122
}; // struct DrawColorSwatch

123
124
struct DrawColorBar : public vtkm::worklet::WorkletMapField
{
125
  using ControlSignature = void(FieldIn, WholeArrayInOut, WholeArrayIn);
126
  using ExecutionSignature = void(_1, _2, _3);
127
128
129
130
131
132
133
134
135
136

  VTKM_CONT
  DrawColorBar(vtkm::Id2 dims, vtkm::Id2 xBounds, vtkm::Id2 yBounds, bool horizontal)
    : Horizontal(horizontal)
  {
    ImageWidth = dims[0];
    ImageHeight = dims[1];
    BarBottomLeft[0] = xBounds[0];
    BarBottomLeft[1] = yBounds[0];
    BarWidth = xBounds[1] - xBounds[0];
137
    BarHeight = yBounds[1] - yBounds[0];
138
139
140
141
142
143
144
145
146
  }

  template <typename FrameBuffer, typename ColorMap>
  VTKM_EXEC void operator()(const vtkm::Id& index,
                            FrameBuffer& frameBuffer,
                            const ColorMap& colorMap) const
  {
    // local bar coord
    vtkm::Id x = index % BarWidth;
Matt Larsen's avatar
Matt Larsen committed
147
    vtkm::Id y = index / BarWidth;
148
    vtkm::Id sample = Horizontal ? x : y;
149
150


151
    const vtkm::Vec4ui_8 color = colorMap.Get(sample);
152

153
154
155
    vtkm::Float32 normalizedHeight = Horizontal
      ? static_cast<vtkm::Float32>(y) / static_cast<vtkm::Float32>(BarHeight)
      : static_cast<vtkm::Float32>(x) / static_cast<vtkm::Float32>(BarWidth);
156
157
158
159
160
    // offset to global image coord
    x += BarBottomLeft[0];
    y += BarBottomLeft[1];

    vtkm::Id offset = y * ImageWidth + x;
161
162
    // If the colortable has alpha values, we blend each color sample with translucent white.
    // The height of the resultant translucent bar indicates the opacity.
163
164
165
166

    constexpr vtkm::Float32 conversionToFloatSpace = (1.0f / 255.0f);
    vtkm::Float32 alpha = color[3] * conversionToFloatSpace;
    if (alpha < 1 && normalizedHeight <= alpha)
167
    {
168
169
170
      constexpr vtkm::Float32 intensity = 0.4f;
      constexpr vtkm::Float32 inverseIntensity = (1.0f - intensity);
      alpha *= inverseIntensity;
171
172
173
174
      vtkm::Vec4f_32 blendedColor(1.0f * intensity + (color[0] * conversionToFloatSpace) * alpha,
                                  1.0f * intensity + (color[1] * conversionToFloatSpace) * alpha,
                                  1.0f * intensity + (color[2] * conversionToFloatSpace) * alpha,
                                  1.0f);
175
176
177
178
      frameBuffer.Set(offset, blendedColor);
    }
    else
    {
Matt Larsen's avatar
Matt Larsen committed
179
      // make sure this is opaque
180
181
182
183
      vtkm::Vec4f_32 fColor((color[0] * conversionToFloatSpace),
                            (color[1] * conversionToFloatSpace),
                            (color[2] * conversionToFloatSpace),
                            1.0f);
184
      frameBuffer.Set(offset, fColor);
185
    }
186
187
188
189
190
191
  }

  vtkm::Id ImageWidth;
  vtkm::Id ImageHeight;
  vtkm::Id2 BarBottomLeft;
  vtkm::Id BarWidth;
192
  vtkm::Id BarHeight;
193
194
195
  bool Horizontal;
}; // struct DrawColorBar

196
} // namespace internal
197

198
199
200
201
202
203
204
struct Canvas::CanvasInternals
{

  CanvasInternals(vtkm::Id width, vtkm::Id height)
    : Width(width)
    , Height(height)
  {
Matt Larsen's avatar
Matt Larsen committed
205
206
207
208
209
210
211
212
213
    BackgroundColor.Components[0] = 0.f;
    BackgroundColor.Components[1] = 0.f;
    BackgroundColor.Components[2] = 0.f;
    BackgroundColor.Components[3] = 1.f;

    ForegroundColor.Components[0] = 1.f;
    ForegroundColor.Components[1] = 1.f;
    ForegroundColor.Components[2] = 1.f;
    ForegroundColor.Components[3] = 1.f;
214
215
216
217
218
  }

  vtkm::Id Width;
  vtkm::Id Height;
  vtkm::rendering::Color BackgroundColor;
Matt Larsen's avatar
Matt Larsen committed
219
  vtkm::rendering::Color ForegroundColor;
220
221
222
223
224
225
226
227
  ColorBufferType ColorBuffer;
  DepthBufferType DepthBuffer;
  vtkm::rendering::BitmapFont Font;
  FontTextureType FontTexture;
  vtkm::Matrix<vtkm::Float32, 4, 4> ModelView;
  vtkm::Matrix<vtkm::Float32, 4, 4> Projection;
};

228
Canvas::Canvas(vtkm::Id width, vtkm::Id height)
229
  : Internals(new CanvasInternals(0, 0))
230
{
231
232
  vtkm::MatrixIdentity(Internals->ModelView);
  vtkm::MatrixIdentity(Internals->Projection);
233
234
235
236
  this->ResizeBuffers(width, height);
}

Canvas::~Canvas()
237
238
{
}
239

240
241
242
243
244
vtkm::rendering::Canvas* Canvas::NewCopy() const
{
  return new vtkm::rendering::Canvas(*this);
}

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
vtkm::Id Canvas::GetWidth() const
{
  return Internals->Width;
}

vtkm::Id Canvas::GetHeight() const
{
  return Internals->Height;
}

const Canvas::ColorBufferType& Canvas::GetColorBuffer() const
{
  return Internals->ColorBuffer;
}

Canvas::ColorBufferType& Canvas::GetColorBuffer()
{
  return Internals->ColorBuffer;
}

const Canvas::DepthBufferType& Canvas::GetDepthBuffer() const
{
  return Internals->DepthBuffer;
}

Canvas::DepthBufferType& Canvas::GetDepthBuffer()
{
  return Internals->DepthBuffer;
}

const vtkm::rendering::Color& Canvas::GetBackgroundColor() const
{
  return Internals->BackgroundColor;
}

void Canvas::SetBackgroundColor(const vtkm::rendering::Color& color)
{
  Internals->BackgroundColor = color;
}

Matt Larsen's avatar
Matt Larsen committed
285
286
287
288
289
290
291
292
293
294
const vtkm::rendering::Color& Canvas::GetForegroundColor() const
{
  return Internals->ForegroundColor;
}

void Canvas::SetForegroundColor(const vtkm::rendering::Color& color)
{
  Internals->ForegroundColor = color;
}

295
296
297
298
299
300
301
302
303
304
void Canvas::Initialize()
{
}

void Canvas::Activate()
{
}

void Canvas::Clear()
{
Matt Larsen's avatar
Matt Larsen committed
305
306
307
  internal::ClearBuffers worklet;
  vtkm::worklet::DispatcherMapField<internal::ClearBuffers> dispatcher(worklet);
  dispatcher.Invoke(this->GetColorBuffer(), this->GetDepthBuffer());
308
309
310
311
312
313
314
315
}

void Canvas::Finish()
{
}

void Canvas::BlendBackground()
{
Matt Larsen's avatar
Matt Larsen committed
316
317
318
  internal::BlendBackground worklet(GetBackgroundColor().Components);
  vtkm::worklet::DispatcherMapField<internal::BlendBackground> dispatcher(worklet);
  dispatcher.Invoke(this->GetColorBuffer());
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
}

void Canvas::ResizeBuffers(vtkm::Id width, vtkm::Id height)
{
  VTKM_ASSERT(width >= 0);
  VTKM_ASSERT(height >= 0);

  vtkm::Id numPixels = width * height;
  if (Internals->ColorBuffer.GetNumberOfValues() != numPixels)
  {
    Internals->ColorBuffer.Allocate(numPixels);
  }
  if (Internals->DepthBuffer.GetNumberOfValues() != numPixels)
  {
    Internals->DepthBuffer.Allocate(numPixels);
  }

  Internals->Width = width;
  Internals->Height = height;
338
339
}

340
341
342
343
void Canvas::AddColorSwatch(const vtkm::Vec2f_64& point0,
                            const vtkm::Vec2f_64& vtkmNotUsed(point1),
                            const vtkm::Vec2f_64& point2,
                            const vtkm::Vec2f_64& vtkmNotUsed(point3),
344
                            const vtkm::rendering::Color& color) const
345
{
346
347
348
349
350
351
352
353
354
355
  vtkm::Float64 width = static_cast<vtkm::Float64>(this->GetWidth());
  vtkm::Float64 height = static_cast<vtkm::Float64>(this->GetHeight());

  vtkm::Id2 x, y;
  x[0] = static_cast<vtkm::Id>(((point0[0] + 1.) / 2.) * width + .5);
  x[1] = static_cast<vtkm::Id>(((point2[0] + 1.) / 2.) * width + .5);
  y[0] = static_cast<vtkm::Id>(((point0[1] + 1.) / 2.) * height + .5);
  y[1] = static_cast<vtkm::Id>(((point2[1] + 1.) / 2.) * height + .5);

  vtkm::Id2 dims(this->GetWidth(), this->GetHeight());
356
357
358
359
360
361

  vtkm::Id totalPixels = (x[1] - x[0]) * (y[1] - y[0]);
  vtkm::cont::ArrayHandleCounting<vtkm::Id> iterator(0, 1, totalPixels);
  vtkm::worklet::DispatcherMapField<internal::DrawColorSwatch> dispatcher(
    internal::DrawColorSwatch(dims, x, y, color.Components));
  dispatcher.Invoke(iterator, this->GetColorBuffer());
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
}

void Canvas::AddColorSwatch(const vtkm::Float64 x0,
                            const vtkm::Float64 y0,
                            const vtkm::Float64 x1,
                            const vtkm::Float64 y1,
                            const vtkm::Float64 x2,
                            const vtkm::Float64 y2,
                            const vtkm::Float64 x3,
                            const vtkm::Float64 y3,
                            const vtkm::rendering::Color& color) const
{
  this->AddColorSwatch(vtkm::make_Vec(x0, y0),
                       vtkm::make_Vec(x1, y1),
                       vtkm::make_Vec(x2, y2),
                       vtkm::make_Vec(x3, y3),
                       color);
}

381
382
void Canvas::AddLine(const vtkm::Vec2f_64& point0,
                     const vtkm::Vec2f_64& point1,
383
                     vtkm::Float32 linewidth,
384
385
                     const vtkm::rendering::Color& color) const
{
386
  vtkm::rendering::Canvas* self = const_cast<vtkm::rendering::Canvas*>(this);
387
  LineRenderer renderer(self, vtkm::MatrixMultiply(Internals->Projection, Internals->ModelView));
388
  renderer.RenderLine(point0, point1, linewidth, color);
389
390
391
392
393
394
395
396
397
398
399
400
}

void Canvas::AddLine(vtkm::Float64 x0,
                     vtkm::Float64 y0,
                     vtkm::Float64 x1,
                     vtkm::Float64 y1,
                     vtkm::Float32 linewidth,
                     const vtkm::rendering::Color& color) const
{
  this->AddLine(vtkm::make_Vec(x0, y0), vtkm::make_Vec(x1, y1), linewidth, color);
}

401
void Canvas::AddColorBar(const vtkm::Bounds& bounds,
402
                         const vtkm::cont::ColorTable& colorTable,
403
                         bool horizontal) const
404
{
405
406
407
408
409
410
411
412
413
414
415
416
  vtkm::Float64 width = static_cast<vtkm::Float64>(this->GetWidth());
  vtkm::Float64 height = static_cast<vtkm::Float64>(this->GetHeight());

  vtkm::Id2 x, y;
  x[0] = static_cast<vtkm::Id>(((bounds.X.Min + 1.) / 2.) * width + .5);
  x[1] = static_cast<vtkm::Id>(((bounds.X.Max + 1.) / 2.) * width + .5);
  y[0] = static_cast<vtkm::Id>(((bounds.Y.Min + 1.) / 2.) * height + .5);
  y[1] = static_cast<vtkm::Id>(((bounds.Y.Max + 1.) / 2.) * height + .5);
  vtkm::Id barWidth = x[1] - x[0];
  vtkm::Id barHeight = y[1] - y[0];

  vtkm::Id numSamples = horizontal ? barWidth : barHeight;
417
  vtkm::cont::ArrayHandle<vtkm::Vec4ui_8> colorMap;
418
419

  {
420
    vtkm::cont::ScopedRuntimeDeviceTracker tracker(vtkm::cont::DeviceAdapterTagSerial{});
421
422
    colorTable.Sample(static_cast<vtkm::Int32>(numSamples), colorMap);
  }
423
424

  vtkm::Id2 dims(this->GetWidth(), this->GetHeight());
425
426
427
428
429
430

  vtkm::Id totalPixels = (x[1] - x[0]) * (y[1] - y[0]);
  vtkm::cont::ArrayHandleCounting<vtkm::Id> iterator(0, 1, totalPixels);
  vtkm::worklet::DispatcherMapField<internal::DrawColorBar> dispatcher(
    internal::DrawColorBar(dims, x, y, horizontal));
  dispatcher.Invoke(iterator, this->GetColorBuffer(), colorMap);
431
432
433
434
435
436
}

void Canvas::AddColorBar(vtkm::Float32 x,
                         vtkm::Float32 y,
                         vtkm::Float32 width,
                         vtkm::Float32 height,
437
                         const vtkm::cont::ColorTable& colorTable,
438
439
440
441
442
443
444
445
                         bool horizontal) const
{
  this->AddColorBar(
    vtkm::Bounds(vtkm::Range(x, x + width), vtkm::Range(y, y + height), vtkm::Range(0, 0)),
    colorTable,
    horizontal);
}

446
447
448
449
vtkm::Id2 Canvas::GetScreenPoint(vtkm::Float32 x,
                                 vtkm::Float32 y,
                                 vtkm::Float32 z,
                                 const vtkm::Matrix<vtkm::Float32, 4, 4>& transform) const
450
{
451
  vtkm::Vec4f_32 point(x, y, z, 1.0f);
452
453
454
  point = vtkm::MatrixMultiply(transform, point);

  vtkm::Id2 pixelPos;
455
456
  vtkm::Float32 width = static_cast<vtkm::Float32>(Internals->Width);
  vtkm::Float32 height = static_cast<vtkm::Float32>(Internals->Height);
457
458
459
460
461
  pixelPos[0] = static_cast<vtkm::Id>(vtkm::Round((1.0f + point[0]) * width * 0.5f + 0.5f));
  pixelPos[1] = static_cast<vtkm::Id>(vtkm::Round((1.0f + point[1]) * height * 0.5f + 0.5f));
  return pixelPos;
}

462
void Canvas::AddText(const vtkm::Matrix<vtkm::Float32, 4, 4>& transform,
463
                     vtkm::Float32 scale,
464
                     const vtkm::Vec2f_32& anchor,
465
                     const vtkm::rendering::Color& color,
466
467
                     const std::string& text,
                     const vtkm::Float32& depth) const
468
{
469
  if (!Internals->FontTexture.IsValid())
470
471
472
473
474
475
476
477
  {
    if (!LoadFont())
    {
      return;
    }
  }

  vtkm::rendering::Canvas* self = const_cast<vtkm::rendering::Canvas*>(this);
478
  TextRenderer fontRenderer(self, Internals->Font, Internals->FontTexture);
479
  fontRenderer.RenderText(transform, scale, anchor, color, text, depth);
480
481
}

482
void Canvas::AddText(const vtkm::Vec2f_32& position,
483
484
485
                     vtkm::Float32 scale,
                     vtkm::Float32 angle,
                     vtkm::Float32 windowAspect,
486
                     const vtkm::Vec2f_32& anchor,
487
488
489
490
491
492
                     const vtkm::rendering::Color& color,
                     const std::string& text) const
{
  vtkm::Matrix<vtkm::Float32, 4, 4> translationMatrix =
    Transform3DTranslate(position[0], position[1], 0.f);
  vtkm::Matrix<vtkm::Float32, 4, 4> scaleMatrix = Transform3DScale(1.0f / windowAspect, 1.0f, 1.0f);
493
  vtkm::Vec3f_32 rotationAxis(0.0f, 0.0f, 1.0f);
494
495
496
497
  vtkm::Matrix<vtkm::Float32, 4, 4> rotationMatrix = Transform3DRotate(angle, rotationAxis);
  vtkm::Matrix<vtkm::Float32, 4, 4> transform =
    vtkm::MatrixMultiply(translationMatrix, vtkm::MatrixMultiply(scaleMatrix, rotationMatrix));

498
  this->AddText(transform, scale, anchor, color, text, 0.f);
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
}

void Canvas::AddText(vtkm::Float32 x,
                     vtkm::Float32 y,
                     vtkm::Float32 scale,
                     vtkm::Float32 angle,
                     vtkm::Float32 windowAspect,
                     vtkm::Float32 anchorX,
                     vtkm::Float32 anchorY,
                     const vtkm::rendering::Color& color,
                     const std::string& text) const
{
  this->AddText(vtkm::make_Vec(x, y),
                scale,
                angle,
                windowAspect,
                vtkm::make_Vec(anchorX, anchorY),
                color,
                text);
}

520
521
bool Canvas::LoadFont() const
{
522
523
  Internals->Font = BitmapFontFactory::CreateLiberation2Sans();
  const std::vector<unsigned char>& rawPNG = Internals->Font.GetRawImageData();
524
525
  std::vector<unsigned char> rgba;
  unsigned long textureWidth, textureHeight;
526
  auto error = DecodePNG(rgba, textureWidth, textureHeight, &rawPNG[0], rawPNG.size());
527
528
529
530
531
532
533
534
535
536
537
  if (error != 0)
  {
    return false;
  }
  std::size_t numValues = textureWidth * textureHeight;
  std::vector<unsigned char> alpha(numValues);
  for (std::size_t i = 0; i < numValues; ++i)
  {
    alpha[i] = rgba[i * 4 + 3];
  }
  vtkm::cont::ArrayHandle<vtkm::UInt8> textureHandle = vtkm::cont::make_ArrayHandle(alpha);
538
  Internals->FontTexture =
539
    FontTextureType(vtkm::Id(textureWidth), vtkm::Id(textureHeight), textureHandle);
540
541
  Internals->FontTexture.SetFilterMode(TextureFilterMode::Linear);
  Internals->FontTexture.SetWrapMode(TextureWrapMode::Clamp);
542
543
544
  return true;
}

545
546
547
548
549
550
551
552
553
554
const vtkm::Matrix<vtkm::Float32, 4, 4>& Canvas::GetModelView() const
{
  return Internals->ModelView;
}

const vtkm::Matrix<vtkm::Float32, 4, 4>& Canvas::GetProjection() const
{
  return Internals->Projection;
}

555
void Canvas::SetViewToWorldSpace(const vtkm::rendering::Camera& camera, bool vtkmNotUsed(clip))
556
{
557
558
  Internals->ModelView = camera.CreateViewMatrix();
  Internals->Projection = camera.CreateProjectionMatrix(GetWidth(), GetHeight());
559
560
}

561
562
void Canvas::SetViewToScreenSpace(const vtkm::rendering::Camera& vtkmNotUsed(camera),
                                  bool vtkmNotUsed(clip))
563
{
564
565
566
  vtkm::MatrixIdentity(Internals->ModelView);
  vtkm::MatrixIdentity(Internals->Projection);
  Internals->Projection[2][2] = -1.0f;
567
568
}

569
void Canvas::SaveAs(const std::string& fileName) const
570
571
{
  this->RefreshColorBuffer();
572
573
574
575
576
577
578
579
580
  auto ends_with = [](std::string const& value, std::string const& ending) {
    if (ending.size() > value.size())
    {
      return false;
    }
    return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
  };

  ColorBufferType::ReadPortalType colorPortal = GetColorBuffer().ReadPortal();
581
582
  vtkm::Id width = GetWidth();
  vtkm::Id height = GetHeight();
583
584
585

  if (ends_with(fileName, ".png"))
  {
586
    std::vector<unsigned char> img(static_cast<size_t>(4 * width * height));
587
588
589
590
591
    for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--)
    {
      for (vtkm::Id xIndex = 0; xIndex < width; xIndex++)
      {
        vtkm::Vec4f_32 tuple = colorPortal.Get(yIndex * width + xIndex);
592
593
        // y = 0 is the top of a .png file.
        size_t idx = static_cast<size_t>(4 * width * (height - 1 - yIndex) + 4 * xIndex);
594
595
596
597
598
599
600
601
602
603
604
605
        img[idx + 0] = (unsigned char)(tuple[0] * 255);
        img[idx + 1] = (unsigned char)(tuple[1] * 255);
        img[idx + 2] = (unsigned char)(tuple[2] * 255);
        img[idx + 3] = (unsigned char)(tuple[3] * 255);
      }
    }

    SavePNG(fileName, img, static_cast<unsigned long>(width), static_cast<unsigned long>(height));
    return;
  }

  std::ofstream of(fileName.c_str(), std::ios_base::binary | std::ios_base::out);
606
607
  of << "P6" << std::endl << width << " " << height << std::endl << 255 << std::endl;
  for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--)
608
  {
609
    for (vtkm::Id xIndex = 0; xIndex < width; xIndex++)
610
    {
611
      vtkm::Vec4f_32 tuple = colorPortal.Get(yIndex * width + xIndex);
612
613
614
      of << (unsigned char)(tuple[0] * 255);
      of << (unsigned char)(tuple[1] * 255);
      of << (unsigned char)(tuple[2] * 255);
615
616
617
618
619
    }
  }
  of.close();
}

620
vtkm::rendering::WorldAnnotator* Canvas::CreateWorldAnnotator() const
621
{
622
  return new vtkm::rendering::WorldAnnotator(this);
623
624
625
}
}
} // vtkm::rendering