1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21#include "Video.h"
22
23// LOVE
24#include "Shader.h"
25#include "Graphics.h"
26
27namespace love
28{
29namespace graphics
30{
31
32love::Type Video::type("Video", &Drawable::type);
33
34Video::Video(Graphics *gfx, love::video::VideoStream *stream, float dpiscale)
35 : stream(stream)
36 , width(stream->getWidth() / dpiscale)
37 , height(stream->getHeight() / dpiscale)
38 , filter(Texture::defaultFilter)
39{
40 filter.mipmap = Texture::FILTER_NONE;
41
42 stream->fillBackBuffer();
43
44 for (int i = 0; i < 4; i++)
45 vertices[i].color = Color32(255, 255, 255, 255);
46
47 // Vertices are ordered for use with triangle strips:
48 // 0---2
49 // | / |
50 // 1---3
51 vertices[0].x = 0.0f;
52 vertices[0].y = 0.0f;
53 vertices[1].x = 0.0f;
54 vertices[1].y = (float) height;
55 vertices[2].x = (float) width;
56 vertices[2].y = 0.0f;
57 vertices[3].x = (float) width;
58 vertices[3].y = (float) height;
59
60 vertices[0].s = 0.0f;
61 vertices[0].t = 0.0f;
62 vertices[1].s = 0.0f;
63 vertices[1].t = 1.0f;
64 vertices[2].s = 1.0f;
65 vertices[2].t = 0.0f;
66 vertices[3].s = 1.0f;
67 vertices[3].t = 1.0f;
68
69 // Create the textures using the initial frame data.
70 auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
71
72 int widths[3] = {frame->yw, frame->cw, frame->cw};
73 int heights[3] = {frame->yh, frame->ch, frame->ch};
74
75 const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
76
77 Texture::Wrap wrap; // Clamp wrap mode.
78 Image::Settings settings;
79
80 for (int i = 0; i < 3; i++)
81 {
82 Image *img = gfx->newImage(TEXTURE_2D, PIXELFORMAT_R8, widths[i], heights[i], 1, settings);
83
84 img->setFilter(filter);
85 img->setWrap(wrap);
86
87 size_t bpp = getPixelFormatSize(PIXELFORMAT_R8);
88 size_t size = bpp * widths[i] * heights[i];
89
90 Rect rect = {0, 0, widths[i], heights[i]};
91 img->replacePixels(data[i], size, 0, 0, rect, false);
92
93 images[i].set(img, Acquire::NORETAIN);
94 }
95}
96
97Video::~Video()
98{
99 if (source)
100 source->stop();
101}
102
103love::video::VideoStream *Video::getStream()
104{
105 return stream;
106}
107
108void Video::draw(Graphics *gfx, const Matrix4 &m)
109{
110 update();
111
112 const Matrix4 &tm = gfx->getTransform();
113 bool is2D = tm.isAffine2DTransform();
114
115 Matrix4 t(tm, m);
116
117 Graphics::StreamDrawCommand cmd;
118 cmd.formats[0] = vertex::getSinglePositionFormat(is2D);
119 cmd.formats[1] = vertex::CommonFormat::STf_RGBAub;
120 cmd.indexMode = vertex::TriangleIndexMode::QUADS;
121 cmd.vertexCount = 4;
122 cmd.standardShaderType = Shader::STANDARD_VIDEO;
123
124 Graphics::StreamVertexData data = gfx->requestStreamDraw(cmd);
125
126 if (is2D)
127 t.transformXY((Vector2 *) data.stream[0], vertices, 4);
128 else
129 t.transformXY0((Vector3 *) data.stream[0], vertices, 4);
130
131 vertex::STf_RGBAub *verts = (vertex::STf_RGBAub *) data.stream[1];
132
133 Color32 c = toColor32(gfx->getColor());
134
135 for (int i = 0; i < 4; i++)
136 {
137 verts[i].s = vertices[i].s;
138 verts[i].t = vertices[i].t;
139 verts[i].color = c;
140 }
141
142 if (Shader::current != nullptr)
143 Shader::current->setVideoTextures(images[0], images[1], images[2]);
144
145 gfx->flushStreamDraws();
146}
147
148void Video::update()
149{
150 bool bufferschanged = stream->swapBuffers();
151 stream->fillBackBuffer();
152
153 if (bufferschanged)
154 {
155 auto frame = (const love::video::VideoStream::Frame*) stream->getFrontBuffer();
156
157 int widths[3] = {frame->yw, frame->cw, frame->cw};
158 int heights[3] = {frame->yh, frame->ch, frame->ch};
159
160 const unsigned char *data[3] = {frame->yplane, frame->cbplane, frame->crplane};
161
162 for (int i = 0; i < 3; i++)
163 {
164 size_t bpp = getPixelFormatSize(PIXELFORMAT_R8);
165 size_t size = bpp * widths[i] * heights[i];
166
167 Rect rect = {0, 0, widths[i], heights[i]};
168 images[i]->replacePixels(data[i], size, 0, 0, rect, false);
169 }
170 }
171}
172
173love::audio::Source *Video::getSource()
174{
175 return source;
176}
177
178void Video::setSource(love::audio::Source *source)
179{
180 this->source = source;
181}
182
183int Video::getWidth() const
184{
185 return width;
186}
187
188int Video::getHeight() const
189{
190 return height;
191}
192
193int Video::getPixelWidth() const
194{
195 return stream->getWidth();
196}
197
198int Video::getPixelHeight() const
199{
200 return stream->getHeight();
201}
202
203void Video::setFilter(const Texture::Filter &f)
204{
205 for (const auto &image : images)
206 image->setFilter(f);
207
208 filter = f;
209}
210
211const Texture::Filter &Video::getFilter() const
212{
213 return filter;
214}
215
216} // graphics
217} // love
218