Граф коммитов

98 Коммитов

Автор SHA1 Сообщение Дата
Bob Owen 261ddce4a8 Bug 1630521: Allow CanvasDrawEventRecorder to control surface destruction recording, so it can be done on the main thread. r=jrmuizel
The recording by CanvasDrawEventRecorder into the ring buffer is not thread-safe
and so must all occur on the same (main) thread.
In addition to that it sometimes needs to send IPC messages via the PCanvas
protocol, which also can only be done on the main thread.

Differential Revision: https://phabricator.services.mozilla.com/D71174
2020-04-16 17:13:16 +00:00
Botond Ballo bb18e79768 Bug 1627480 - Fix non-unified build errors in gfx/2d. r=tnikkel
Differential Revision: https://phabricator.services.mozilla.com/D69729

--HG--
extra : moz-landing-system : lando
2020-04-04 23:48:48 +00:00
Jeff Muizelaar 44a1d84d88 Bug 1617708. Make ClippedDrawTarget use destination DT. a=reland
By using the destination DT we will use the correct offset
during playback instead of the offset of the reference target.

Differential Revision: https://phabricator.services.mozilla.com/D68495

--HG--
extra : rebase_source : cff20f1a467138e0d9fe3e22772bb3edbb409318
extra : source : 0e08a1d7fb078cc36882b737f00da2f48f9349a6
2020-03-27 03:15:05 +00:00
Sebastian Hengst 4a470594ac Backed out changeset 0e08a1d7fb07 (bug 1617708) for likely causing performance regression bug 1625071.
--HG--
extra : rebase_source : f912c901608e5783abb93607f963d29b035e5c84
2020-03-30 17:30:44 +02:00
Jeff Muizelaar b5242e1d14 Bug 1617708. Make ClippedDrawTarget use destination DT.
By using the destination DT we will use the correct offset
during playback instead of the offset of the reference target.

Differential Revision: https://phabricator.services.mozilla.com/D68495

--HG--
extra : moz-landing-system : lando
2020-03-27 03:15:05 +00:00
Andrew Osmond 91b071ed14 Bug 1618345 - Enforce proper color management by splitting gfx::Color into sRGBColor and DeviceColor types. r=jrmuizel
gfx::Color is currently misused in many places. The DrawTargets expect
the color space to be in device space, e.g. what we are actually going
to draw using. Everything sitting above generally deals with sRGB, as
specified in CSS. Sometimes we missed the conversion from sRGB to device
space when issuing draw calls, and similarly sometimes we converted the
color to device space twice.

This patch splits the type in two. sRGBColor and DeviceColor now
represent sRGB and device color spaces respectively. DrawTarget only
accepts DeviceColor, and one can get a DeviceColor from an sRGBColor via
the ToDeviceColor helper API. The reftests now pass with color
management enabled for everything (e.g. CSS) instead of just tagged
raster images.

There will be a follow up patch to enable color management everywhere by
default on all supported platforms.

Differential Revision: https://phabricator.services.mozilla.com/D64771

--HG--
extra : moz-landing-system : lando
2020-03-09 14:16:17 +00:00
Andrew Osmond fbaa72bc71 Bug 1617493 - Rasterize SVG patterns in content process into a shared surface for WebRender. r=jrmuizel
In DrawTargetRecording::CreateSimilarDrawTarget, we would originally
create a new DrawTargetRecording. For resources that are shared, such as
SVG patterns, we would do the same work for each blob image tile. This
can get expensive if the pattern is large. Now we check a size
threshold, and if passed, we create a DrawTargetSkia backed by a
SourceSurfaceSharedData. When we want a snapshot, we now try to get the
shared surface instead if available. The recording infrastructure
already knows how to handle the lifetimes and use of external IDs for
shared surfaces, so now we rasterize the pattern once for all the blob
tiles. In an ideal world we would do this in the compositor process
once, but that requires more changes, and this is useful as a stopgap in
the meantime.

Differential Revision: https://phabricator.services.mozilla.com/D63903

--HG--
extra : moz-landing-system : lando
2020-02-25 13:56:23 +00:00
Tim Nguyen 08ab9ceb1e Bug 1616587 - Implement conic-gradient for Skia graphics backend. r=lsalzman
Differential Revision: https://phabricator.services.mozilla.com/D63415

--HG--
extra : moz-landing-system : lando
2020-02-21 00:12:47 +00:00
Csoregi Natalia ff5e1dafb1 Backed out changeset bf981458da0c (bug 1616587) for bustage on DrawTargetCairo.cpp. CLOSED TREE 2020-02-21 02:07:11 +02:00
Tim Nguyen edf9be08c3 Bug 1616587 - Implement conic-gradient for Skia graphics backend. r=lsalzman
Differential Revision: https://phabricator.services.mozilla.com/D63415

--HG--
extra : moz-landing-system : lando
2020-02-20 23:50:06 +00:00
Tim Nguyen 0afd3695fa Bug 1616803 - Fix copy-paste error in DrawTargetRecording.cpp. r=gfx-reviewers,jrmuizel
Differential Revision: https://phabricator.services.mozilla.com/D63489

--HG--
extra : moz-landing-system : lando
2020-02-20 16:02:13 +00:00
Bob Owen 60b9eebd75 Bug 1604800 Part 2: Remove DataSourceSurfaceRecording from DrawTargetRecording. r=jrmuizel
This is done by using CreateDataSourceSurfaceWithStrideFromData to create a
DataSourceSurface and then OptimizeSourceSurface to record and optimize that.
This means that we now only have SourceSurfaceRecording using SurfaceType
RECORDING. We then rely on this to improve the test in OptimizeSourceSurface to
check that a SourceSurfaceRecording is for its recorder.

Differential Revision: https://phabricator.services.mozilla.com/D60736

--HG--
extra : moz-landing-system : lando
2020-01-22 21:23:08 +00:00
Bob Owen 76aff2ff48 Bug 1604180: Use original surface for GetDataSurface after DrawTargetRecording::OptimizeSourceSurface. r=jrmuizel
We return a SourceSurfaceRecdording from OptimizeSourceSurface to represent
the optimized surface. However, GetDataSurface might be called on this
SourceSurfaceRecdording, so we hold the original SourceSurface we optimized in
the SourceSurfaceRecdording to return its GetDataSurface.

Differential Revision: https://phabricator.services.mozilla.com/D59712

--HG--
extra : moz-landing-system : lando
2020-01-16 15:53:08 +00:00
Bob Owen 2efd34ce2b Bug 1598582 Part 2: Add event to record OptimizeSourceSurface. r=jrmuizel
This also optimizes the surface returned by CreateSourceSurfaceFromData to
match what would happen in the non-recorded case. This means that all surfaces
of type RECORDING are optimized, i.e. they represent a surface type that
matches the DrawTarget type in the Translator.

Differential Revision: https://phabricator.services.mozilla.com/D55292

--HG--
extra : moz-landing-system : lando
2019-11-29 21:59:11 +00:00
Bob Owen a9a7c894d9 Bug 1582372 Part 2: Only clean up Canvas IPC objects when recorder has no stored objects. r=jrmuizel
The recorder can still contain UnscaledFonts, but they are never removed
currently, so we can't wait for them and they will be re-recorded safely anyway.

Depends on D49757

Differential Revision: https://phabricator.services.mozilla.com/D49758

--HG--
extra : moz-landing-system : lando
2019-10-29 08:34:34 +00:00
Bob Owen 003b93beee Bug 1580511: Check size is allowed in DrawTargetRecording::CreateSourceSurfaceFromData. r=jrmuizel
Differential Revision: https://phabricator.services.mozilla.com/D48317

--HG--
extra : moz-landing-system : lando
2019-10-07 14:38:06 +00:00
Bob Owen 4ad6464c7c Bug 1575844: Allow for null/empty GradientStops in Moz2D recording code. r=jrmuizel
Differential Revision: https://phabricator.services.mozilla.com/D43074

--HG--
extra : moz-landing-system : lando
2019-08-30 17:14:22 +00:00
Jeff Muizelaar bcd10ea75c Bug 1561743. DrawTargetRecording IntRect. r=nical
This lets us record at arbitratry offsets. Without it gfxContext::GetClip()
would break because it uses DrawTarget::GetRect() as the initial rect that it
intersects subsequent clips with. We also can't just use a DrawTargetOffset
because that applies a transform to the inner DrawTarget and will impact the
recorded commands.

Differential Revision: https://phabricator.services.mozilla.com/D37075

--HG--
extra : moz-landing-system : lando
2019-07-10 13:49:08 +00:00
Jeff Muizelaar 0b68652b49 Bug 1539702. Improve CreateClippedDrawTarget API r=jwatt,rhunt
This changes CreateClippedDrawTarget so that instead of taking
a max size and a transform it just takes a user space rect of
the desired bounds.

This change allows the caller to not worry about the computing
a max size based on the current clip. Instead this responsibility
is lowered into the specific backends.

The main motivation for this work is to allow blob recoordination
to create recordings that don't depend on the current clip.

Some additional benefits are that the API is easier to use and
as can be seen simplifies the SVG masking code because it doesn't
need to track surface offsets manually.

It's also an important step towards removing all the uses of
gfxContext::GetClipExtents which will let us get rid of the separate
clipping stack in gfxContext and help us move off of gfxContext
completely.

Most backend implementations of CreateClippedDrawTarget are relatively
simple. DrawTargetCapture is modified to track the current clip rect
so that it can create a new DrawTargetCapture of the appropriate size
without needing to worry about lazy resolution.

Differential Revision: https://phabricator.services.mozilla.com/D33363

--HG--
extra : moz-landing-system : lando
2019-06-21 09:51:00 +00:00
Narcis Beleuzu 767a7b2616 Backed out changeset 88e6c989e783 (bug 1539702) for multiple reftest failures. CLOSED TREE 2019-06-21 12:46:16 +03:00
Jeff Muizelaar afe9ee5a45 Bug 1539702. Improve CreateClippedDrawTarget API r=jwatt,rhunt
This changes CreateClippedDrawTarget so that instead of taking
a max size and a transform it just takes a user space rect of
the desired bounds.

This change allows the caller to not worry about the computing
a max size based on the current clip. Instead this responsibility
is lowered into the specific backends.

The main motivation for this work is to allow blob recoordination
to create recordings that don't depend on the current clip.

Some additional benefits are that the API is easier to use and
as can be seen simplifies the SVG masking code because it doesn't
need to track surface offsets manually.

It's also an important step towards removing all the uses of
gfxContext::GetClipExtents which will let us get rid of the separate
clipping stack in gfxContext and help us move off of gfxContext
completely.

Most backend implementations of CreateClippedDrawTarget are relatively
simple. DrawTargetCapture is modified to track the current clip rect
so that it can create a new DrawTargetCapture of the appropriate size
without needing to worry about lazy resolution.

Differential Revision: https://phabricator.services.mozilla.com/D33363

--HG--
extra : moz-landing-system : lando
2019-06-20 22:34:31 +00:00
Bob Owen 2853ef8068 Bug 1464032 Part 11: Make SourceSurface from DrawTargetRecording::CreateSourceSurfaceFromData hold its data. r=jrmuizel
This means that GetData on the returned SourceSurface will function correctly.
2018-12-02 14:17:24 +00:00
Bob Owen 7d566c46fe Bug 1464032 Part 4: Record DrawTarget::Flush and DrawTarget::DetachAllSnapshots. r=jrmuizel 2018-12-02 14:14:11 +00:00
Sylvestre Ledru e226046cb8 Bug 1547143 - Format the tree: Be prescriptive with the pointer style (left) r=Ehsan
# ignore-this-changeset

Depends on D28954

Differential Revision: https://phabricator.services.mozilla.com/D28956

--HG--
extra : moz-landing-system : lando
2019-05-01 08:47:10 +00:00
Jean-Yves Avenard 11ac9e9cf8 Bug 1540581 - P6. Tidy some C++ declarations in gfx/. r=gerald,jrmuizel
* Remove redundant virtual keywords
* Mark all destructors of inheriting classes as virtual for clarity
* Mark all classes without virtual destructor as final (exposed errors)
* Make destructor virtual where it needed to be (some were missing)
* Replace empty ({}) code declaration in header with = default
* Remove virtual unused methods

I probably missed some, it quickly became a rabbit hole.

Differential Revision: https://phabricator.services.mozilla.com/D26060

--HG--
extra : moz-landing-system : lando
2019-04-11 12:36:51 +00:00
Chris Peterson e77882620f Bug 1528881 - Part 4: gfx/2d: Make some global functions static. r=lsalzman
And #include "BufferUnrotate.h" in BufferUnrotate.cpp for BufferUnrotate() function prototype.

clang's -Wmissing-prototypes option identifies global functions that can be made static (because they're only called from one compilation unit) or removed (if they're never called).

gfx/2d/BufferUnrotate.cpp:17:6 [-Wmissing-prototypes] no previous prototype for function 'BufferUnrotate'
gfx/2d/DrawTargetCairo.cpp:195:6 [-Wmissing-prototypes] no previous prototype for function 'ReleaseData'
gfx/2d/DrawTargetCairo.cpp:201:18 [-Wmissing-prototypes] no previous prototype for function 'CopyToImageSurface'
gfx/2d/DrawTargetCairo.cpp:239:18 [-Wmissing-prototypes] no previous prototype for function 'GetAsImageSurface'
gfx/2d/DrawTargetCairo.cpp:251:18 [-Wmissing-prototypes] no previous prototype for function 'CreateSubImageForData'
gfx/2d/DrawTargetCairo.cpp:272:18 [-Wmissing-prototypes] no previous prototype for function 'ExtractSubImage'
gfx/2d/DrawTargetCairo.cpp:308:18 [-Wmissing-prototypes] no previous prototype for function 'GetCairoSurfaceForSourceSurface'
gfx/2d/DrawTargetRecording.cpp:26:6 [-Wmissing-prototypes] no previous prototype for function 'RecordingSourceSurfaceUserDataFunc'
gfx/2d/DrawTargetRecording.cpp:272:6 [-Wmissing-prototypes] no previous prototype for function 'RecordingFontUserDataDestroyFunc'
gfx/2d/DrawTargetWrapAndRecord.cpp:26:6 [-Wmissing-prototypes] no previous prototype for function 'WrapAndRecordSourceSurfaceUserDataFunc'
gfx/2d/DrawTargetWrapAndRecord.cpp:358:6 [-Wmissing-prototypes] no previous prototype for function 'WrapAndRecordFontUserDataDestroyFunc'
gfx/2d/FilterNodeSoftware.cpp:1816:6 [-Wmissing-prototypes] no previous prototype for function 'IsAllZero'
gfx/2d/FilterNodeSoftware.cpp:183:37 [-Wmissing-prototypes] no previous prototype for function 'CloneAligned'
gfx/2d/MacIOSurface.cpp:442:6 [-Wmissing-prototypes] no previous prototype for function 'MacIOSurfaceBufferDeallocator'
gfx/2d/QuartzSupport.mm:38:6 [-Wmissing-prototypes] no previous prototype for function 'cgdata_release_callback'
gfx/2d/ScaledFontMac.cpp:191:10 [-Wmissing-prototypes] no previous prototype for function 'CalcTableChecksum'
gfx/2d/ScaledFontMac.cpp:224:5 [-Wmissing-prototypes] no previous prototype for function 'maxPow2LessThan'
gfx/2d/unittest/TestCairo.cpp:12:6 [-Wmissing-prototypes] no previous prototype for function 'TryCircle'

Differential Revision: https://phabricator.services.mozilla.com/D20263

--HG--
extra : rebase_source : b42bce33ed899caeb89e462d99a5cde29a9bb559
extra : intermediate-source : 7923cc86a6394bcd2fb3b7e38f458a5180d31e11
extra : source : 02388f2454e8842d2c023bf9f9fab222d8130093
2019-02-16 18:08:30 -08:00
Jeff Muizelaar 0c609e94c7 Bug 1522021. Propagate input size to CreateSimilarDrawTarget for filters. r=mstange
This lets us avoid drawing the complete input for ever tile when
drawing filters into a tile.

Differential Revision: https://phabricator.services.mozilla.com/D17686

--HG--
extra : moz-landing-system : lando
2019-01-27 23:36:04 +00:00
Sylvestre Ledru 265e672179 Bug 1511181 - Reformat everything to the Google coding style r=ehsan a=clang-format
# ignore-this-changeset

--HG--
extra : amend_source : 4d301d3b0b8711c4692392aa76088ba7fd7d1022
2018-11-30 11:46:48 +01:00
Kartikaya Gupta 17eea57296 Bug 1466613 - Robustify DrawTargetRecording codepaths that create new drawtargets. r=mstange
Badly-behaved consumers of DrawTargetRecording can trigger recording of
draw calls that will fail to allocate required draw targets when the
recording is replayed. This patch tries to guard against this by
detecting these situations at record-time rather than crashing at
replay-time. When such a situation is detected, it will crash (for
content processes, to catch such scenarios) or gracefully fail (for
other processes).

Differential Revision: https://phabricator.services.mozilla.com/D11527

--HG--
extra : moz-landing-system : lando
2018-11-13 10:39:02 +00:00
Ryan Hunt fd979d2851 Bug 1475139 part 7 - Add DrawDependentSurface API to DrawTarget. r=mattwoodrow
This commit adds an API to DrawTarget to draw a surface that will be provided
at the time a recording is replayed. The surface is referenced using a user
interpreted ID.

This will be used for drawing a OOP iframe, and the ID will be the TabId.

Differential Revision: https://phabricator.services.mozilla.com/D6786

--HG--
extra : rebase_source : d5ce9b429c89e9adb0e5fb180f60125e64f12d4a
2018-09-24 21:43:41 -05:00
Lee Salzman de75729420 Bug 1480615 - reuse scaled fonts across blob image recordings. r=jrmuizel 2018-09-05 21:55:53 -04:00
Emilio Cobos Álvarez fffb25b74f Bug 1465585: Switch from mozilla::Move to std::move. r=froydnj
This was done automatically replacing:

  s/mozilla::Move/std::move/
  s/ Move(/ std::move(/
  s/(Move(/(std::move(/

Removing the 'using mozilla::Move;' lines.

And then with a few manual fixups, see the bug for the split series..

MozReview-Commit-ID: Jxze3adipUh
2018-06-01 10:45:27 +02:00
Andrew Osmond 63d93fdc78 Bug 1425484 - Part 3. Integrate shared surfaces with the blob image recordings. r=jrmuizel 2018-04-26 19:00:16 -04:00
Jeff Muizelaar 4338f2052b Bug 1388842. Add support for updating blob images. r=mstange
Currently, we use a simple merging algorithm, because the more
complicated ones didn't work.

This code won't actually be used until we do blob image invalidation
in a follow up.

MozReview-Commit-ID: Q2Em3QC195
2018-03-20 10:30:26 -04:00
Lee Salzman b63d990b24 Bug 1440559 - don't multiply DataSourceSurfaceRecording stride by bpp. r=jrmuizel
MozReview-Commit-ID: LvvB9HlF01h
2018-02-23 10:37:32 -05:00
Jeff Muizelaar 62a9686ee2 Bug 1439005. Add PushLayerWithBlend. r=Bas
This makes it possible to implement nsDisplayBlend blob image invalidation.
It currently only includes an implementation for Skia and DrawTargetRecording.
All other backends will crash when used.

MozReview-Commit-ID: 2GhdDxi4jHG
2018-02-17 12:07:30 -05:00
Jamie Nicol 61cdff34c5 Bug 1429508 - Allow created mask surfaces to be clipped to the necessary size when replaying a recording. r=bas
Add a command CreateClippedDrawTarget to DrawTarget, which takes the
max required size and a transform between this draw target and the one
to be created. The created draw target may have its size clipped to
the size of this draw target, transformed to the new target's
space. This means that the new surface will be large enough so
that it is rendered to this draw target correctly, but not necessarily
any larger.

Usually this will just create a draw target of the requested size, for
simplicity. However, when replaying a recorded draw target we do clip
the size to the base draw target's size. This is done using a
DrawTargetTiled, so when applying the mask in PopLayer, we must take
the SourceSurface's offset in to account.

MozReview-Commit-ID: 89ONElphzLu

--HG--
extra : rebase_source : 7eebeb66a2686a7b6f4ade36f3004ebb06abc2fe
2018-02-05 17:59:42 +00:00
Lee Salzman 7cf35e807f Bug 1436375 - add RecordedScaledFontCreation event in DrawTargetRecording::FillGlyphs. r=jrmuizel
MozReview-Commit-ID: 780xX36HeYH
2018-02-07 15:48:23 -05:00
Chris Peterson 37efe4d0e6 Bug 1428535 - Add missing override specifiers to overridden virtual functions. r=froydnj
MozReview-Commit-ID: DCPTnyBooIe

--HG--
extra : rebase_source : cfec2d96faeb11656d86d760a34e0a04cacddb13
extra : intermediate-source : 6176724d63788b0fe8caa3f91607c2d93dbaa7ec
extra : source : eebbb0600447f9b64aae3bcd47b4be66c02a51ea
2017-11-05 19:37:28 -08:00
Markus Stange 78303f13ce Bug 1414154 - Remove GlyphRenderingOptions. r=lsalzman
MozReview-Commit-ID: JtwnTj1hhPV

--HG--
extra : rebase_source : 2a3e896eec8b6839097baf6e2cccc10eb162827b
2017-11-02 21:42:56 -04:00
Daniel Holbert 126bd9e1a4 Bug 1412427 part 8: (automated patch) Switch a bunch of C++ files in gfx to use our standard mode lines. r=jrmuizel
This patch was generated automatically by the "modeline.py" script, available
here: https://github.com/amccreight/moz-source-tools/blob/master/modeline.py

For every file that is modified in this patch, the changes are as follows:
 (1) The patch changes the file to use the exact C++ mode lines from the
     Mozilla coding style guide, available here:
https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Coding_Style#Mode_Line

 (2) The patch deletes any blank lines between the mode line & the MPL
     boilerplate comment.

 (3) If the file previously had the mode lines and MPL boilerplate in a
     single contiguous C++ comment, then the patch splits them into
     separate C++ comments, to match the boilerplate in the coding style.

MozReview-Commit-ID: 77D61xpSmIl

--HG--
extra : rebase_source : c6162fa3cf539a07177a19838324bf368faa162b
2017-10-27 16:10:06 -07:00
Jeff Muizelaar f1e1608327 Bug 1380014. Add the ability to record UnscaledFonts by index. r=lsalzman 2017-10-27 18:21:26 -04:00
Sylvestre Ledru 7c0ae251cd Bug 1381253 - Remove redundant control flow declarations rs=ehsan
MozReview-Commit-ID: FFxP4aMCbOL

--HG--
extra : amend_source : 3aec108430b11048f47ffe19d5da7ac5034770a9
2017-07-15 19:03:04 +02:00
Jeff Muizelaar bb066b19f2 Bug 1377362. Make the constructors to the recording events generic. r=lsalzman 2017-07-06 07:15:40 -04:00
Jeff Muizelaar 4f96690374 Bug 1374900. Make our low level serialization functions generic over the type of stream. r=lsalzman 2017-06-22 23:55:47 -04:00
Jeff Muizelaar 64116b92d4 Bug 1362245. Stop running commands during recording for BlobImage. r=lsalzman
This keeps around mFinalDT but mostly doesn't use it (except for when creating
a PathBuilder). All playback is ripped out and we'll no longer hold references
to things to mFinalDT::GraidentStops or SourceSurfaces etc.
2017-06-12 17:53:21 -04:00
Jeff Muizelaar 63724b1070 Bug 1369552. Split out DrawTargetWrapAndRecord and DrawTargetRecording. r=Bas
DrawTargetRecording will stop playing back durin recording.

--HG--
rename : gfx/2d/DrawTargetRecording.cpp => gfx/2d/DrawTargetWrapAndRecord.cpp
rename : gfx/2d/DrawTargetRecording.h => gfx/2d/DrawTargetWrapAndRecord.h
extra : rebase_source : fd41326974905946318489cc801929328331ae73
2017-06-12 16:52:29 -04:00
Jeff Muizelaar 07374b97fe Bug 1372225 - Add recording for IntoLumaSource. r=mchang
This lets us avoid using the default implementation and allows us to
do the actual work on during playback.
2017-06-12 16:15:08 -04:00
Jeff Muizelaar 0442ca87fb Bug 1367538. Clear user data after we're done with the recorder. r=lsalzman
This helps keep us from accumulating all of the recorders.

The basic idea is to track weak references to the SourceSurfaces and Fonts that
we add UserData to in DrawEventRecorderPrivate and then clear these UserData's
when we're done recording.

This adds a RemoveAndDestroy helper to UserData to make this possible.
2017-06-09 14:31:35 -04:00
Lee Salzman 28471864af Bug 1355931 - move font serialization from ScaledFont to UnscaledFont. r=jrmuizel
MozReview-Commit-ID: 3d1XMoe2BKj
2017-04-14 14:11:00 -04:00